Wednesday, March 4, 2009

Adventures while building a Silverlight Enterprise application part #8

Well, it's been a while again. Sorry about that, but it's been really hectic around here.
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)
{
IEnumerable<BusinessObjectBase> result;
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);
}
return result;
}


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.

6 comments:

  1. Have you looked at ADO.NET DataServices ?

    They play nicely with EF and , IMO, provide a service layer

    ReplyDelete
  2. That's a good question, steveg. Our most important reason for this was that, at the time, there were some security issues with DataServices. As security is one of our priorities, this was not an acceptable alternative.

    I'm not sure if these issues have been resolved, but as we are getting into full swing development, we don't want to change technology choices at this level.

    Thanks for your feedback.

    Greets,
    Jonathan

    ReplyDelete
  3. Hi Jonathan,

    What kind of security issues did you run into when considering ADO.NET DataServices? We're considering using it, any insight appreciated.

    Greg

    ReplyDelete
  4. Hi Greg,

    I didn't do this part of the research myself, but as far as I can recall all request data was send by using the HTTP GET method, meaning nothing in the request could be encrypted. I could be wrong here, so if you're going to use this info in any decision making, please verify it first (and if you do, it would be nice to let people know the result :-) ).

    Greets,
    Jonathan

    ReplyDelete
  5. Ah OK, yes, that's the first thing we saw when we looked at it (although it's not just GET, it uses the other HTTP verbs too).

    However, as far as we know it can work over HTTPS, and also I believe it can use the WCF security infrastructure. We've not proved this yet though, so this is just speculation on my part....when we do look into it properly, maybe I can share some info here.

    We have a bigger issue at the moment, in that Silverlight only supports basicHttpBinding and not wsHttpBinding when connecting to our WCF services which we're currently trying to work through though!

    Cheers,
    Greg

    ReplyDelete
  6. I know. We are dealing with the same issue of SL2 only supporting basicHttpBinding. We are planning a test with HTTPS and besides that, we're implementing our own security measures, which I'm unable to share with you right now.

    As I think about it, and see where we are going with our service layer. I don't think dataservices would have worked for us. We plan on tuning in the data that is send to our SL2 client to the point that only one request is made to provide data to a complete module and I don't see how this would have been possible with dataservices.

    Thanks for your valuable feedback, Greg.

    Greets,
    Jonathan

    ReplyDelete