Published: 18 Nov 2008
By: Dino Esposito
Download Sample Code

Dino Esposito talks about complex types in LINQ-to-SQL.

Contents [hide]

Introduction

A few months ago I wrote on DotNetSlackers about making the LINQ-to-SQL object model a bit more abstract. You can read the full story here. The article was about adding custom properties to a LINQ-to-SQL object model of some complex, non-scalar type. In the companion example, I just defined a Unit custom property of type UnitInfo to group a couple of classic database column properties such as QuantityPerUnit and UnitPrice.

As it often happens with demo applications you show at conferences, or use for books and articles, the sample code worked fine for me but not for some readers. More precisely, the source code provided worked like a champ in its original, unedited form. A few readers edited the code while a few others attempted to reproduce the underlying idea and concept in their own applications. With some changes coming around, the magic of having LINQ-to-SQL to support complex types vanished.

This month I’m back to explain what was wrong with that code and to provide a fix. Hopefully, this time the code will survive readers’ changes. If correct, we can all say that now a nice hack exists to make LINQ-to-SQL support complex types in some way. And, more importantly, without breaking the Visual Studio 2008 designer.

Complex Types in LINQ-to-SQL

By design, LINQ-to-SQL provides a table-based object model with a 1:1 match between entities in the model and tables or views in the related database. It goes without saying that all properties in any objects are to be scalar types such as strings, dates, or perhaps numbers.

In rich, commercial O/RM tools, this limitation doesn’t apply. You typically design your own object model independently from the details of the underlying data store. You use collections and complex types at your leisure. When you’re done, you map properties to some physical database. This is the way it works with, say, NHibernate and other O/RM tools such as LLBLGen Pro. (Incidentally, this isn’t the way in which things go with Microsoft’s Entity Framework v1.)

LINQ-to-SQL has a very well defined goal and, subsequently, a neat set of features. In addition, it is extremely (and quite easily) extensible; at least in the way you design and implement classes in the model. By leveraging this innate ability of LINQ-to-SQL, you can bend it using and supporting properties of complex types—a feature that is not documented and officially not supported. If you ask around, most people will tell you that it’s not a supported feature (which is true), but also impossible to obtain; which is, as you’ll see in a moment, untrue.

Custom Properties of Complex Types

As mentioned, you can read here the full story about the steps required to define a complex type property in LINQ-to-SQL. For your convenience, I’m summarizing the key facts. Suppose you pick up the Product table from the Northwind database and come up with only a few properties: ProductID, ProductName, UnitPrice, and QuantityPerUnit. All properties contain values of some primitive types.

Now let’s begin the magic tour of LINQ-to-SQL extensibility by deciding that, all in all, we want to group two of those properties in a new wrapper property—Unit. Our clear purpose is successfully compiling and executing the following code:

The first thing to do is to remove from the Product designer class the properties we want to group together. You just select them and delete using the visual tools of the Visual Studio 2008 designer. You should expect to have a Product entity that looks like in the figure below.

Figure 1: The initial Product entity.

The initial Product entity.

To enter all required extensions, you take advantage of the partial class mechanism and add a new product.cs file to the project. In this file, you define the new property Unit and some other things. The property Unit will be of a custom type for which you still owe the Visual Studio 2008 project a proper definition. Here’s a possible one:

In the product.cs file, you need to do two main things. One is providing a definition for the public Unit property. Another is providing bindable properties that the LINQ-to-SQL engine can map to database columns. In other words, you still need properties for UnitPrice and QuantityPerUnit; you just want them to be private and not visible at the consumer’s level. These two properties, for example, will be decorated by the [Column] attribute which is the tool that binds them to LINQ-to-SQL persistence. Here’s some code to have in product.cs.

Finally, you need to bring in the definition of some partial methods used in the setter methods of the above properties.

This is where we left in the June article. In any consumer code, you can brilliantly read data through the new API and, nicely enough, you can even update these properties. However, and that was the issue, you can update only in memory. None of your programmatic changes, in fact, will ever be detected as a change to the model that needs persistence to the database.

Did You Deliver Code That Doesn’t Work?

The source code that I used to proof my points looked like this:

The code worked smoothly. However, when some readers tried to port the concept to another database they happened to write a slightly different consumer code—even simpler, like the one below:

Believe it or not, commenting out the update on a property that is not involved with the changes to the model we made really breaks the code. Why is it so? And what can you do about it?

Why Is It So?

The DataContext class has a nice property named GetChangeSet. The property returns the (read-only) collection of updated objects. In other words, if an object is not referenced in the collection it will not be persisted to the database. For some weird reason, the commented line above makes the difference. If you update a “regular” LINQ-to-SQL property before attempting to update a custom property, the entity object is added to the list of changed objects. Otherwise, it won’t.

But there’s more that attentive readers of the article didn’t report. If you move the update of a “regular” property around—say after updating the custom property—things go even worse. In this case, in fact, you a get conflict exception when the execution flow reaches the SubmitChanges method.

What’s going on?

The SendPropertyChanged method (defined in the designer code) fails to find a registered handler for the PropertyChanged event featured by all classes (like entities) that expose the INotifyPropertyChanged interface. For reasons that have to do with the internal implementation of LINQ-to-SQL (which doesn’t officially support complex types…) no handler to take care of property changes is registered for non-scalar properties on an entity class.

So if you go first through a change that turns on the modified flag on an instance, then it all works. That’s why if you first update, say, ProductName it all works. ProductName is handled regularly and changes the state of the entity. When submitting changes, the entity is marked as modified and all of its properties decorated with [Column] are read. QuantityPerUnit and UnitPrice original properties, although private now, are still there and their getters and setters are linked to analogous properties on UnitInfo. This makes the trick.

So in the end, it all works if you update a non-scalar property before you attempt to make any changes to the members of a complex type property.

What Can You Do?

The trick consists in adding a scalar property (say, an integer) for each complex type property you plan to have in a LINQ-to-SQL object model. I suggest you call it xxxProperty if xxx is your complex type property. You will add this property to the partial class to make it independent from the designer behavior and rules. If you add a property through the designer, it will be given the [Column] attribute which we don’t want here. Editing the designer’s code is painful as it gets modified every time you save the designer’s output. For a Unit property, you’ll have a UnitProperty private helper property, as below:

The implementation is just the plain vanilla implementation of a LINQ-to-SQL scalar property. I designed this property to be an integer. The type can be everything as this property only serves the purpose to signal changes on another property. I find that integers or Booleans work great. If you could update this property before you make any changes to the members of the complex type property, you’re fine.

However, from the consumer’s code working on a UnitProperty property may not be a good thing to see. Let’s add a public method then, as below:

Now the sample code using the Product entity looks like the following:

Summary

Entity Framework—the Microsoft’s announced successor to LINQ-to-SQL won’t support complex type properties before v2. In the current v1, you can change the type of an entity property to a complex .NET type by editing the source code of the Entity Data Model. In doing so, though, you would break the Visual Studio 2008 designer. Your code, though, would work just great.

This is to say that properties of custom types in an object model are an important feature to have—even in a simple O/R framework like LINQ-to-SQL.

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Dino Esposito

Dino Esposito is one of the world's authorities on Web technology and software architecture. Dino published an array of books, most of which are considered state-of-the-art in their respective areas. His most recent books are “Microsoft ® .NET: Architecting Applications for the Enterprise” and “...

This author has published 53 articles on DotNetSlackers. View other articles or the complete profile here.

Other articles in this category


C# 4.0 Reflection Programming - Part 3
In the previous article, we used the reflection to obtain the information of an assembly, module, ty...
C# 4.0 Reflection Programming - Part 4
In this last article of this series, we will learn what to do with reflection. But before making the...
C# 4.0 Reflection Programming - Part 2
As introduced in the first article, the most typically-used tools associated with .NET reflection ar...
Understanding and Using Extension Methods
Extension methods were new to C# 3.0. They allow you to add a method to an existing type without hav...
Introduction to C# 3.0 features
C# 3.0 introduced some of very useful features built on top of 2.0. This article explains the usage,...

You might also be interested in the following related blog posts


RadScheduler for Silverlight learning series, part 3: Add, Edit, and Delete Appointments read more
Complex Types in the EDM Designer in EF4 and a look at updating complex types in code read more
An Extensive Examination of LINQ: Using the Query Syntax read more
OleDb Parameters to access FoxPro Data from .NET read more
Create custom LINQ providers fluently read more
Logging Entity Framework Queries to Look for Perf Improvement Opportunities read more
Get SOLID: Single Responsibility Principle read more
Using the Select LINQ query operator with indexes read more
date validations read more
A few things you can't do with EF queries which you won't find out until runtime read more
Top
 
 
 

Please login to rate or to leave a comment.

Free Agile Project Management Tool from Telerik
TeamPulse Community Edition helps your team effectively capture requirements, manage project plans, assign and track work, and most importantly, be continually connected with each other.