Wednesday, March 25, 2009

Adventures while building a Silverlight Enterprise application part #10

Yeah! It's part 10 of my Adventure series. Sorry, I'm not giving a party or anything, but it feels like a milestone to me.

In this episode we are going to look at an issue I ran into with WCF and Silverlight 2 when using a specific structure in the datacontract. In our case, we wanted to use a Dictionary that would contain another Dictionary as the value. Have a look at our data contract to have a clearer picture:


[DataContract]
[KnownType(typeof(Dictionary<string, object>))]
public class CompositeType
{
private Dictionary<string, object> _properties;
private string _stringValue = "Hello ";

public CompositeType()
{
_properties = new Dictionary<string, object>();
}

[DataMember]
public string StringValue
{
get { return _stringValue; }
set { _stringValue = value; }
}

[DataMember]
public Dictionary<string, object> Properties
{
get
{
return _properties;
}
}
}

As you can see, the dictionary type is a part of our DataContract, trough the use of the DataMember attribute. We also added that same type to our known types list, trough the use of the KnownTypes attribute. We could have used the ServiceKnownTypeAttribute on our interface type as well, with the same result.

If we would now use a Silverlight 2 client with a service reference to this service and make a call to our service, everything seems fine...

...until, that is, we use a Dictionary instance as a value in the Properties property.
In that case an exception occurs, stating:

There was an error while trying to deserialize parameter http://tempuri.org/:GetDataUsingDataContractResult. The InnerException message was 'Error in line 1 position 584. Element 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:Value' contains data of the 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfKeyValueOfstringanyType' data contract. The deserializer has no knowledge of any type that maps to this contract. Add the type corresponding to 'ArrayOfKeyValueOfstringanyType' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.

If we wouldn't have added the dictionary type to our known types list, the service call would have timed out, without a proper exception, indicating that some problem would have occurred in the service.

I've been digging to see if I could come up with a solution. Googling the exception I got plenty of examples with derived types, but non with nested types and late binding, like I'm using. After some reading on datacontracts and digging into the reference code in the client, I figured that known types in the service might just not be distributed to the client. To prove my theory I started looking at the generated service client instance at runtime. I looked in the EndPoint property which contains the contract, which in turn contains the operations for the contract. As known types are usually scoped at the operations level, I checked to see if it contained any items in the KnownTypes property and in fact it didn't. This means my theory is correct and I should add the dictionary type to the known types list of the operation. As I didn't want to do this in generated code, I decided to do it at runtime:


private Service1Client GetServiceClient()
{
Service1Client client = new Service1Client();

foreach (var operation in client.Endpoint.Contract.Operations)
{
operation.KnownTypes.Add(typeof(Dictionary<string, object>));
}

return client;
}


If I now run the client, everything runs like a charm. What we actually did, by adding these known types, is telling the DataContractSerializer to include our dictionary type when deserializing any incoming xml messages.

You can download some sample code here:


I hope this helps any of you encountering the same issue. If you have any questions or comments, please leave me a comment below.

Wednesday, March 18, 2009

Adventures while building a Silverlight Enterprise application part #9

Well, here we are again, already for part #9 of our series on using Silverlight in an Enterprise application. Last time we looked at some of the tricky bits on using Entity Framework in our application.


This time we want to look at a particular databinding technique, that we tend to use a lot on forms in our application. We want to look at lookup data and using classes in lookup data. What I mean is using a combobox to select some object from a list that describes part of our own data.

Below is an image of what I'm trying to do here:



As you can see we have a datagrid with computers. Whenever I select a record from the grid, this record is displayed in the form. But the casing combobox will not show me what item is selected, although I did databind to the SelectedItem property and they are the same type. The problem is that they are not the same instance.

Now if I would replace the Casing type with string and fill the combobox with a list of strings, all would work fine. This actually gives away a simple but important fact about how matching is done to select the item in the combobox. It uses some sort of comparison. So let's implement the most obvious form of comparison to make sure that equal casings actually say that they are equal, by overriding the Equals method.

public class Casing
{
public string Name { get; set; }

public override bool Equals(object obj)
{
Casing casing = obj as Casing;
if (casing != null)
{
return casing.Name.Equals(Name, StringComparison.OrdinalIgnoreCase);
}
return false;
}

public override int GetHashCode()
{
return Name.GetHashCode();
}
}

As you can see I've also overridden GetHashCode. As Visual Studio will indicate, it's best practice to always override both of the methods at the same time, so sorting follows the same logic as the Equals method. As we use the Name property to evaluate if two casings are the same, it's only logical to also use the Name properties hashcode.


Now if you look at the application, it looks like this:


Mission successful. Conclusion: if you want to be able to use lookups, always override the Equals method on your business objects.

You can download this example project below:


I hope this article was helpful for you and I look forward to reading your comments and answering any questions.

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.