I'm using a distributed web application, with a database server housing the SQL Server 2005 database, an application server where we've installed a Microsoft Three Tier Service Application, and a web server where I using MVP with supervising controller.
My service layer, on the application server, returns an IEnumerable<Country> when I request a list of let's say countries. The service layer calls the data access component, which yields entities like this:
public virtual IEnumerable<T> FillList(DbCommand command, DataContext context)
{
using (IDataReader dataReader = this.ExecuteReader(command, context))
{
while (dataReader.Read())
{
T entity = this.Fill(dataReader, context);
yield return entity;
}
}
}
I'm now a little bit concerned about the connection to my database, as it will remain open when I serialize entities through WCF to my controller. My first question is if this is really a concern to leave my database connection open when serializing entity by entity to my controller? The advantage of this solution is that I can use LINQ even over large collections (not LINQ to SQL).
I came up with this solution: in the service layer, I always return a List, like this:
public virtual List<T> GetList()
{
List<T> list = new List<T>();
list.AddRange(this.dataAccess.GetList()));
return list;
}
But here I'm returning a full list to the controller, and perhaps I only need a few of the items from the list. My second question is if this is a good design?
Look forward to your suggestions!
I don't see any particular problems with the design.
Of course, you'll want to be careful about what the T is in List<T>. Don't return a list of objects with all sorts of behaviors, and don't return types specific to .NET - DataSet and DataTable are a bad idea, for instance. Use simple classes with properties of primitive types, lists and arrays of primitive types, lists and arrays of these simple classes, etc.
Related
We are considesing using BreezeJS as analogous solution to our current WCF Ria Services + Silverlight pair. In WCF Ria Services we have client DataContext containing EntitySet instances which get populated with queries.
For example, there we might have three methods on our DomainService described below. Assume also that User is linked to Order as 1-to-many:
public IEnumerable<User> GetFirst10Users(){
//retrieve from DB with EF
return ObjectContext.Users.OrderBy(u => u.Id).Take(10);
}
public IEnumerable<User> GetLast10Users(){
//retrieve from DB with EF
return ObjectContext.Users.OrderByDesc(u => u.Id).Take(10);
}
public IEnumerable<Order> GetOrders(){
//retrieve from DB with EF
return ObjectContext.Orders;
}
As a result we'll have something like this generated for us on client-side:
public class Context{
public EntitySet<User> Users {...};
public EntitySet<Order> Orders {...};
public EntityQuery<User> GetFirst10UsersQuery(){
}
public EntityQuery<User> GetLast10UsersQuery(){
}
public EntityQuery<Orders> GetOrdersQuery(){
}
}
If we execute first two queries, we'll end up having union of their results in Users collection. Executing the third one will give us all orders in Orders collection, but only subset of those will be contained in Navigation collections of User instances on client. It works really well and as Entities get added to Users collection(by query, or by user's action, or by other viewmodel's side effect) those get displayed on UI right away through binding to ObservableCollection wrapper over Users EntitySet.
The same gets reflected on canceling changes, and I don't need to do a lot for that - just providing observable interface.
With BreezeJS and JS particularly, it looks like that I should be subscribing to various events of Breeze and synchronize data with my Kendo UI observable array instance (which is in fact copying data between breeze cache and Kendo UI observable array).
In WCF Ria Services, Context.Users entityset has EntityAdded / EntityDeleted events which makes it possible to handle additions/removals in our ViewModels.
The same events are also available for NavigationCollections, so we might do user.Orders.EntityAdded += ... for example.
So, I'm trying to implement something similar to what we had in WCF Ria Services + Silverlight using Angular + BreezeJS + Kendo UI in TypeScript. I have kind of found analogs for EntityAdded/Deleted events for EntitySet in Breeze but didn't find anything like this for NavigationCollections.
Could someone let me know if the path I'm trying to go with is completely awkward in BreezeJS/KendoUI/Angular world? If no, how do I handle changes in navigation collections in BreezeJS?
Following advices from people on the internet about service references, I got rid of them now and split the service/data contracts into a common assembly accesible by both the server and the client. Overall this seems to work really well.
However I’m running into problems when trying to use custom objects, or rather custom subtypes, in the service. Initially I wanted to define only interfaces in the common assembly as the contract for the data. I quickly learned that this won’t work though because the client needs a concrete class to instantiate objects when receiving objects from the service. So instead I used a simple class instead, basically like this:
// (defined in the common assembly)
public class TestObject
{
public string Value { get; set; }
}
Then in the service contract (interface), I have a method that returns such an object.
Now if I simply create such an object in the service implementation and return it, it works just fine. However I want to define a subtype of it in the service (or the underlying business logic), that defines a few more things (for example methods for database access, or just some methods that work on the objects).
So for simplicity, the subtype looks like this:
// (defined on the server)
public class DbTestObject : TestObject
{
public string Value { get; set; }
public DbTestObject(string val)
{
Value = val;
}
}
And in the service, instead of creating a TestObject, I create the subtype and return it:
public TestObject GetTestObject()
{
return new DbTestObject("foobar");
}
If I run this now, and make the client call GetTestObject, then I immediately get a CommunicationException with the following error text: “The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:09:59.9380000'.”
I already found out, that the reason for this is that the client does not know how to deserialize the DbTestObject. One solution would be to declare the base type with the KnownTypeAttribute to make it know about the subtype. But that would require the subtype to be moved into the common assembly, which is of course something I want to avoid, as I want the logic separated from the client.
Is there a way to tell the client to only use the TestObject type for deserialization; or would the solution for this be to use data transfer objects anyway?
As #Sixto Saez has pointed out, inheritance and WCF don't tend to go together very well. The reason is that inheritance belongs very much to the OO world and not the messaging passing world.
Having said that, if you are in control of both ends of the service, KnownType permits you to escape the constraints of message passing and leverage the benefits of inheritance. To avoid taking the dependency you can utilise the ability of the KnownTypeAttribute to take a method name, rather than a type parameter. This allows you to dynamically specify the known types at run time.
E.g.
[KnownType("GetKnownTestObjects")]
[DataContract]
public class TestObject
{
[DataMember]
public string Value { get; set; }
public static IEnumerable<Type> GetKnownTestObjects()
{
return Registry.GetKnown<TestObject>();
}
}
Using this technique, you can effectively invert the dependency.
Registry is a simple class that allows other assemblies to register types at run-time as being subtypes of the specified base class. This task can be performed when the application bootstraps itself and if you wish can be done, for instance, by reflecting across the types in the assembly(ies) containing your subtypes.
This achieves your goal of allowing subtypes to be handled correctly without the TestObject assembly needing to take a reference on the subtype assembly(ies).
I have used this technique successfully in 'closed loop' applications where both the client and server are controlled. You should note that this technique is a little slower because calls to your GetKnownTestObjects method have to be made repeatedly at both ends while serialising/deserialising. However, if you're prepared to live with this slight downside it is a fairly clean way of providing generic web services using WCF. It also eliminates the need for all those 'KnownTypeAttributes' specifying actual types.
I have a problem when sending Entity Framework-generated entities with navigation properties over WCF. I have a Securities database for storing financial data and two tables inside it:
Stock : Id, Symbol, CompanyName, ExchangeName
Option: Id, StockId, OptionType, Strike
I created an Entity Framework model for this database. Then I created WCF service which exposes generated Stock and Option EF entities to the clients.
My generated entity Stock has navigation property EntityCollection<Option> Options.
When trying to return Stock entity from WCF service to the client, I get an SerializationException: WCF cannot serialize Options navigation property because database connection has been already closed.
I can call Options.Load() method when database connection is opened to fill Options property, but what should I do if I don't want to load full object graph for Stock entity?
I've fought with this one for a while.
First, I turned lazy loading off. But I still had problems with cycles in my object graph.
Then, I put [DataContract(IsReference = true)] tags on all of my entities. That worked, but I still had a lot of performance issues do to a denormalized database.
Finally, I broke down and make dtos and I use AutoMapper to populate them.
One of my coworkers told me to do this from the beginning, and I should have just listened to him. Do yourself a favor and don't make the same mistake that I did.
Edit
I forgot to mention that I had issues deserializing entities that has properties of type ICollection<T>. These will deserialize as arrays. Somehow T[] implements ICollection<T> but Add and Remove will throw exceptions. This was another reason to use DTOs.
With EF5, not sure with EF4, this might work. Change the navigation property setter to private.
public class OptionEntity
{
// properties
// navigation
public virtual StockEntity Stock { get; private set; }
}
If a project has used POCO for entities and uses entity framework and uses lazy loading then you have an "incomplete" object graph going back over the wire. So when a client uses the Entity is there some sort of proxy that will automagically load the remaining values? Do we have to create this proxy ourselves and wrap the original entity in it? Or is there an accepted pattern for identifying lazy loaded types which would then signal the client to make another call to the WCF?
Lazy loading with WCF usually doesn't work because your method looks like:
public List<MyPoco> GetData()
{
using (var context = new MyObjectContext())
{
return context.MyPocos.ToList();
}
}
As you see context is closed in method (you have to close context somewhere). But when the list is serialized it will try to lazy load dependent objects => exception because context is already closed. In WCF you should use eager loading.
Use flat DTO's, you probably don't want to expose your full domain to the client anyway. WCF is message-based, not Domain Driven.
I have a method in my web service which returns a DataView,
I have setup a proxy which talks to this service but when i make this method in the proxy
public DataView GetSales(DateTime SalesDate)
{
ServiceClient client = new ServiceClient();
return client.GetSalesForDay(SalesDate);
}
I get the error "Cannot implicitly convert type 'object[]' to 'System.Data.DataView', i have tried googling this but not getting anywhere, any help would be much appreciated.
Thanks
You can't do this - you cannot and should not return something like a DataView from a WCF service ever. A WCF service would only ever return data - not objects with behavior (DataView contains a lot of behavior - sorting, filtering, etc.).
Instead, in your service code, do this:
query your database with a SqlDataReader
parse out the relevant info you really need (only those fields you're really interested in) into DTO's (Data Transfer Objects) - basically just plain objects holding nothing but the properties of a "sale" that are important to you
return a List from your WCF service
Instead of doing steps 1 and 2 yourself, you could also use Linq-to-SQL, NHibernate, or any other capable ORM to handle that conversion from row/columns in the database to an object for you.