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.

Wednesday, October 22, 2008

Silverlight 2.0 on Silverlight Streaming

I spent this afternoon trying to post my beta of Catalyst Blockbuster (a tetris-like game developed in Silverlight 2.0). However, I was unable to get the Silverlight application hosted properly, and as such I removed the post for the time being.

It turns out the issues I was having were due to problems with Silverlight Streaming. I registered for an account and followed the directions to host a Silverlight application. However, whenever I tried to display my Silerlight 2.0 application through the iframe the service provides, I kept seeing the "Install Microsoft Silverlight" placeholder. After doing some extensive googling, I found the following thread on the Silverlight.net forums:

http://silverlight.net/forums/p/11060/115525.aspx#115525

It turns out that Silverlight Streaming does not currently support Silverlight 2.0 applications or streams. That thread does not contain information as to when the service will be updated. When it is updated, I will post the beta of Catalyst Blockbuster!

Friday, October 3, 2008

LINQ to SQL: Deleting Entities in Memory

In my previous post, I demonstrated how to use DeleteOnNull in LINQ to SQL database markup language (dmbl) association definitions in order to delete relational objects. However, DeleteOnNull is not the complete solution when working with relational objects in memory. The following example will better explain the situation where DeleteOnNull does not work.

In my database, I have an OrderDetail table that has a One-to-Many relationship with a Shipments table. Both of the business objects were generated using the Visual Studio 2008 dbml designer. In addition, I have the DeleteOnNull attribute for the association between the tables set to true.

Based upon the products in a given Order, my system will create appropriate Shipment objects. An example of constructing a shipment object and adding it directly to an OrderDetail is shown below:

1 OrderDetail od = new OrderDetail();

2

3 Shipment ship = new Shipment();

4

5 ship.Cost = 20.00M;

6 ship.Weight = 10.00M;

7 ship.TrackingNumber = "ABCD123456789";

8

9 od.Shipments.Add(ship);

In my application, the shipment objects are generated automatically depending on the products in the customer's shopping cart. Once the Shipment objects have been created, the user managing the order has the option to custom configure how many boxes and their weights should be shipped in order to reduce cost. The Shipment object is added directly to the OrderDetail's shipment collection.

The list of shipment objects were bound to a DataGridView with editing and deleting enabled. The DataSource of the DataGrid was set to od.Shipments.ToList(). However, I quickly discovered that despite the DeleteOnNull attribute being set to true for the association between OrderDetails and Shipments, attempting to delete a Shipment throws an InvalidOperationException with the following message:

"An attempt was made to remove a relationship between a OrderDetail and a Shipment. However, one of the relationship's foreign keys (Shipment.OrderDetailId) cannot be set to null."

After doing some research on the msdn forums, I discovered that the DeleteOnNull attribute does not work for items created in memory that are not attached directly to an Entity's source table. The ultimate solution in this case was to use the DataGridView's UserDeletingRow event with the following code:

1 private void shipmentDataGridView_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)

2 {

3 Shipment temp = e.Row.DataBoundItem as Shipment;

4 _context.Shipments.Attach(temp);

5 _context.Shipments.DeleteOnSubmit(temp);

6 }


_context is the DataContext on which the Shipment object was originally created. By attaching the Shipment entity directly to the Shipments table and calling DeleteOnSubmit on the attached Shipment, the DataContext is able to successfully delete the Shipment object.

Wednesday, October 1, 2008

LINQ to SQL: DeleteOnNull

My first project at Catalyst just wrapped up this past week, and it involved creating an order fulfillment system using Microsoft .Net 3.5 WinForms technology.

For our data layer, we went ahead and used LINQ to SQL to interact with an installation of Microsoft SQL Server. Most of our business objects were generated via the designer for our DataContext.dbml file in Microsoft Visual Studio 2008. We decided to use LINQ to SQL with hopes of speeding up the development cycle. For the most part, LINQ to SQL fit our needs very well. However there were a few troublesome issues that I would like to see addressed in some form in future releases.

One of the issues I ran into several times while creating business object, was how to delete entities with associations in a relational database. When attempting to delete an entity that contains an association with another object in the database, by default the DataContext throws an InvalidOperationException, with a message similar to the following:

"An attempt was made to remove a relationship between a OrderDetail and a OrderLineItem. However, one of the relationship's foreign keys (OrderLineItem.OrderDetailId) cannot be set to null."

The relationship between an OrderDetail and OrderLineItem is one to many. By default, the DataContext attempts to set the OrderLineItem.OrderDetailId to null. However, we want the object to be removed from the database completely.

The solution to the issue is to set the Association's DeleteOnNull attribute to true. Unfortunately, this attribute cannot be set within the designer. Instead, the attribute must be added by hand within the DataContext.cs.designer file. An example if this implementation is below:


[Association(Name="OrderDetail_OrderLineItem", Storage="_OrderDetail", ThisKey="OrderDetailId", OtherKey="OrderDetailId", IsForeignKey=true, DeleteOnNull=true)]

Further explanations and examples of this issue can be found on Beth Massi's blog and Dinesh Kulkarni's blog.