At the moment we are digging into the back end of our application. You may remember from previous episodes that we use WCF services in our back end. One of the most important services in our project is our Business Layer Service. This service provides access on our database through ADO.NET Entity Framework.
So that's great. We can generate our data layer code, because we use EF, but how about the rest of our code? As we have a large amount of entities, one thing we didn't want to do was to provide service calls for each of them. It would be a lot of boring work, with high maintenance code as a result. So how would we make this generic?
Well, the WCF part of things isn't that hard. We defined a base class that provides us with a property bag. As we pass an instance of this class over the line, all that is send is a dictionary of keys and values. All our derived classes do, is access the property bag from the base class through properties. These derived classes, we generate from a meta data store.
Loading these objects from the EF is not as straight forward, but still not that much of an issue. We simply provide a string with the name of the object we want to the service, which we use to load the object through the EF method GetObjectByKey. We then reflect trough the properties of the resulting objects and fill the property bag of our generic class.
The real problems start when you want to provide a generic way to build lists of objects. We looked at many possible solutions, only to come up blank. Eventually it hit me, we needed a hybrid solution, between generic code and generated code. So I went looking for a solution using facilities provided in our base class to get to a list of specific EF entities and here is what I came up with:
public static IEnumerable<BusinessObjectBase> CreateList(Dictionary<string, object> criteria)
string queryText = GetQueryText("MyEntity", criteria);
using (CobraXeContext context = new CobraXeContext())
ObjectParameter parameters = GetParametersFromCriteria(criteria);
ObjectQuery<MyEntity> query = context.CreateQuery<MyEntity>(queryText.ToString(), parameters);
ObjectResult<MyEntity> objectResult = query.Distinct().Execute(MergeOption.PreserveChanges);
result = GetListFromObjectResult(objectResult);
On the service this looks easy, but actually getting to this point proved quite a challenge. Especially generating an EF query that worked, even if I needed to filter on properties from a different, but related entity was difficult. The documentation on joining in EF SQL is vague when it comes to it's exact syntax and it's also not completely intuitive as how to get to related information.
Eventually I came up with the following rule for building a query, which is implemented in the method above:
select value [entity] from [entity] join [jointable] on [entity].[joinproperty]=[jointable] where [predicates]
In my code I've assumed that the name of a joinproperty is always equal to the jointable name.
After building this and having my business classes regenerated, I started wondering about what code is to be generic, what is to be generated and what code is specific enough to be developed by hand, for a specific use. After thinking about this for several days, I came to the conclusion that there is no straight answer to this question. Maybe this decision is what has become part of our 'art', as I like to call it. Each situation is different and each program is different. I've worked on code for three months, only to have it run for two hours and never again. I've also worked on code for an hour and saw the same code being used years after.
Yep, it's been an interesting week. I hope you have learned as much as I have and I'm looking forward to read your comments.