Thursday, November 13, 2008

LINQ to SQL: Force update on entity

In my last project, we used LINQ to SQL for both the data access layer and the business logic layer. The entities generated in a DBML file (database markup language) are partial classes. This allowed for implementing business logic directly into the objects by extending the classes' partial definitions. This allowed for rapid development once the data model was established in SQL Server 2005. However, during the project, there were quite a few problems with using LINQ to SQL generated entities for the business objects as I have mentioned in some of my previous blog posts. The most recent problem I ran into involved the lack of support for change tracking properties defined outside of the designer generated file.

In my solution, I ran into a situation in which I wanted to force an Update on an entity. Since I used the DBML designer generated entities to store my business logic, in several of these objects I added new properties to the partial class definitions. I then used stored procedures to save the changes in these new properties to the database. Since all of the CRUD (create, update, delete) operations are handled by the DBML, I designed the system that the update stored procedures for custom properties would be fired in the parent objects Update method (for instance, partial void UpdateAddress(Address instance)). However, I quickly discovered that LINQ to SQL's change tracking is only done on the properties implemented in the designer file.

For instance, if I have an Order table in my database, the designer generates a partial Order class. I extend my order class by adding a CustomerAddress object property. Due to databinding and some UI features I want to implement, I want the Customer and Address information to be displayed and databound to a single object. Therefore, a CustomerAddress object contains all of the properties found in the Customer and Address designer generated classes. When a user makes a change to a property in the CustomerAddress object, ideally this would trigure that the parent object, the Order, would be marked as "dirty" and it would require an update. However, LINQ to SQL change tracking does not support this functionality.

At first, I attempted to fix this problem by having my externally defined properties raise SendPropertyChanged("PropertyName") in the set definition of the property. For instance, when a property in the CustomerAddress property in the Order object is changed, the Order object should raise that the CustomerAddress changed. Therefore, an Update for the Order object should appear in the DataContext.GetChangeSet(). However, this does not work.

The second thought that came to mind was that perhaps exists a method such as "UpdateOnSubmit", similar to InsertOnSubmit and DeleteOnSubmit. However, LINQ to SQL does not support provide a forced Update method.

Ultimately, the solution that worked involved modifying the data model and adding a "LastChange" DateTime column to my Order table. Whenever the CustomerAddress property is modified, the LastChange property is set to the current DateTime. This will trigger an Update on the Order object in the DataContext change set. This enabled the effect I desired: the Order object would be marked as "dirty", causing UpdateOrder(Order instance) to be called. My stored procedures that implemented saving / updating CustomerAddress objects were then executed from within UpdateOrder(Order instance).

If anyone has any questions, or any alternative suggestions, I would appreciate the feedback in the comments!

Thursday, November 6, 2008

Dynamically create WCF service clients.

I'm currently studying for the Microsoft .Net Framework 3.5 WCF certification exam. One of the concepts that we've been discussing around the office recently is how to properly consume self-developed WCF services. Over the past few weeks, Josh Heyse has hammered into my brain the fact that I should not being adding service references to services encapsulated within a solution.

Despite the fact that adding a service reference is incredibly easy, it can lead to complications if the service is modified or changed. Service references use metadata (wsdl) to regenerate service contracts and proxies. Because the service contracts in the client are actual copies, they don't automatically change when the original service contract changes. If my service definition changed while developing, I would have to force refreshes on my service references to generate updated proxies, and errors are harder to catch at compile time. Therefore, when you have access to the dll in which the ServiceContracts are defined, it is better to dynamically create your clients using a ChannelFactory and the ServiceContract definition.

In light of all this, I've decided to create a simple guide to dynamically create WCF clients using a ChannelFactory. In addition, I'm going to demonstrate how to create WCF clients based off of endpoint information stored in a configuration file.

Step 1: Create a Windows Class Library Project named Contracts . This is where we will store all of the ServiceContracts and DataContracts for our WCF service. In this example I'm going to create a simple math service, with the service contract defined by an interface named IMathService. Below is a copy of the code for this service definition.

2 namespace Contracts

3 {

4 [ServiceContract]

5 public interface IMathService

6 {

7 [OperationContract]

8 int AddInt(int a, int b);

9

10 [OperationContract]

11 int SubtractInt(int a, int b);

12

13 [OperationContract]

14 double AddDouble(double a, double b);

15

16 [OperationContract]

17 double SubtractDouble(double a, double b);

18 }

19 }

Step 2: In the same solution, create a WCF Service Library named SampleMathService. Add the Contracts project as a reference to this project. Delete the default Service1.cs and IService1.cs files. Instead, add a new class file with the name MathService.cs. In this new class, add "using Contracts;" to the top, and implement the IMathService interface. Below is a copy of the code:

1 namespace SampleMathService

2 {

3 public class MathService : IMathService

4 {

5 #region IMathService Members

6

7 public int AddInt(int a, int b)

8 {

9 return a + b;

10 }

11

12 public int SubtractInt(int a, int b)

13 {

14 return a - b;

15 }

16

17 public double AddDouble(double a, double b)

18 {

19 return a + b;

20 }

21

22 public double SubtractDouble(double a, double b)

23 {

24 return a - b;

25 }

26

27 #endregion

28 }

29 }

For this example, I configured the the MathService using the default wsHttpBinding and granted it the address of http://localhost:8888/MathService/. Below is the configuration file:

1 xml version="1.0" encoding="utf-8" ?>

2 <configuration>

3 <system.web>

4 <compilation debug="true" />

5 system.web>

6

8 <system.serviceModel>

9 <services>

10 <service behaviorConfiguration="SampleMathService.MathServiceBehavior"

11 name="SampleMathService.MathService">

12 <endpoint address="" binding="wsHttpBinding" contract="Contracts.IMathService">

13 <identity>

14 <dns value="localhost" />

15 identity>

16 endpoint>

17 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />

18 <host>

19 <baseAddresses>

20 <add baseAddress="http://localhost:8888/MathService/" />

21 baseAddresses>

22 host>

23 service>

24 services>

25 <behaviors>

26 <serviceBehaviors>

27 <behavior name="SampleMathService.MathServiceBehavior">

28 <serviceMetadata httpGetEnabled="true" />

29 <serviceDebug includeExceptionDetailInFaults="false" />

30 behavior>

31 serviceBehaviors>

32 behaviors>

33 system.serviceModel>

34 configuration>

There is an endpoint for the MetaData in this configuration, however we will not be using that since we have access to the ServiceContract definition (Contracts.IMathService)!

Step 3: Add a Windows Console Application to your project. Add the Contracts project as a reference to this project. In addition, add the System.ServiceModel library as a reference to this project. This is where we will create and test our dynamic client.

In order to allow for the service configuration to be modified outside of compiled code, add a configuration file to your project. Within this App.config file, we need to add the client and service endpoint information within the System.ServiceModel configuration section. My App.config is displayed below:

1 xml version="1.0" encoding="utf-8" ?>

2 <configuration>

3 <system.serviceModel>

4 <client>

5 <endpoint name="MathServiceEndpoint"

6 address="http://localhost:8888/MathService/"

7 contract="Contracts.IMathService"

8 binding="wsHttpBinding"/>

9 client>

10 system.serviceModel>

11 configuration>

I named my endpoint MathServiceEndpoint, and the address, contract and binding information are all defined for when we create our client dynamically. This is a useful step because it allows for us to change endpoint information in the future without having to modify any code.

We are now ready to go ahead and code our test client and application. The code to create a client dynamically is incredibly simple, especially since we have all of our endpoint information defined within our App.Config file. The System.ServiceModel namespace contains a ChannelFactory object, which allows for the creation of channels that can connect with the service endpoint. To create a create a client all we have to do is initalize a ChannelFactory that passes in the service's ServiceContract. Using this ChannelFactory, we can create a new channel to use as our proxy for communication with our WCF service. The code that demonstrates this is below:

1 ChannelFactory<IMathService> factory = new ChannelFactory<IMathService>("MathServiceEndpoint");

2

3 IMathService proxy = factory.CreateChannel();

Once we have created our proxy channel, we are able to call the OperationContracts defined in our IMathService ServiceContract. Below is a copy of my Console Application, which calls each of the OperationContracts in IMathService.

1 namespace MathClientTestApplication

2 {

3 class Program

4 {

5 static void Main(string[] args)

6 {

7 ChannelFactory<IMathService> factory = new ChannelFactory<IMathService>("MathServiceEndpoint");

8

9 factory.Open();

10

11 IMathService proxy = factory.CreateChannel();

12

13 // Test AddInt(int a, int b)

14 int addIntResult = proxy.AddInt(3, 2);

15 Console.WriteLine(string.Format("AddInt(3, 2) = {0}", addIntResult));

16

17 // Test SubtractInt(int a, int b)

18 int subtractIntResult = proxy.SubtractInt(3, 2);

19 Console.WriteLine(string.Format("SubtractInt(3, 2) = {0}", subtractIntResult));

20

21 // Test AddDouble(double a, double b)

22 double addDoubleResult = proxy.AddDouble(4.5, 5.5);

23 Console.WriteLine(string.Format("AddDouble(4.5, 5.5) = {0}", addDoubleResult));

24

25 // Test SubtractDouble(double a, double b)

26 double subtractDoubleResult = proxy.SubtractDouble(9.5, 6.1);

27 Console.WriteLine(string.Format("SubtractDouble(9.5, 6.1) = {0}", subtractDoubleResult));

28

29 factory.Close();

30

31 Console.ReadLine();

32

33 }

34 }

35 }

As you can see, I call each of the Operations that IMathService offers, and print the results to the console. In addition, I added factory.Open() and factory.Close() to explicitly indicate when creating channels is available and when the factory should be disposed.

I hope you enjoyed this simple guide to consuming WCF services dynamically! If you have any questions, feel free to leave them in the comments suggestion.

Monday, November 3, 2008

Catalyst Blockbuster Beta

I've recently begun to explore Microsoft's Silverlight 2.0 and WPF. Once I started reading up on Silverlight, I decided that creating a simple game would be a fun way to start the learning process. The first game that came to mind was Tetris.

Using Silverlight 2.0, I was able to create the following tetris-like application called Catalyst Blockbuster in about 16 hours. It is a beta version, and it does contain a few bugs. However, I have started an internal project related to this, during which I am rewriting much of the code, so I don't plan up updating this version further.

It is built on Silverlight 2.0, which can be downloaded at the following link: http://www.microsoft.com/silverlight/. I am hosting this application using Silverlight Streaming, which was recently upgraded to support Silverlight 2.0.

Click here to play Catalyst Blockbuster Beta 1!

Special thanks to the author of WPFTetris from here: http://code.msdn.microsoft.com/WPFTetris/Release/ProjectReleases.aspx?ReleaseId=382. I browsed through his code to get an idea of how to start my application.