A Software Engineering Blog

by Nick Holmes

  Home  |   Contact  |   Syndication    |   Login
  11 Posts | 0 Stories | 6 Comments | 0 Trackbacks

News

Archives

Post Categories

Wednesday, May 06, 2009 #

To use LINQ To SQL, we need a Data Context object to provide our point of entry. It might be possible to use a System.Data.Linq.DataContext object directly, but its more usual to derive from this class to make a database specific version. Additionally, we need classes to represent our "entities". These define the mapping to the tables (or views) and columns.

We could code all this up by hand, but Visual Studio 2008 comes with the grandly named Object Relational Designer that makes short work of this. This is a graphical tool to define our data context and entities, and a code generator to spit out some C# when we're done. The designer edits a .dbml file that defines the mapping, and the code generation is based on this, so it easy to refine these mappings as we go. There is also a command line tool, SqlMetal.exe, for those working without Visual Studio, or wanting to set up more complex build processes.

I don't want to spend time on this part of the process; it's exactly the same as if it was being done for consumption in C#, and there is plenty of coverage on MSDN. I will be posting the code for all of this soon.

I'll be using the Microsoft AdventureWorks sample database, which can be downloaded here. This first test is based on the Contact table. The data entity class will also be called Contact. The web service will then be returning instances of this class - in other words, it will be our WCF Data Contract.

How then, are we going to get the WCF DataContract attribute onto this generated class? Happily, the LINQ team wondered about that too, and added a "Serialization Mode" flag to the O/R Designer. Its values are "None" and the curiously named "Unidirectional", but the latter is the one we want.

A quick look at the generated class confirms this. Here's a snippet to give you the idea of what's generated:

    [Table(Name="Person.Contact")]
    [DataContract()]
    public partial class Contact : INotifyPropertyChanging, INotifyPropertyChanged
    {
        private string _Title;

        [Column(Storage="_Title", DbType="NVarChar(8)")]
        [DataMember(Order=3)]
        public string Title
        {
            get
            {
                return this._Title;
            }
            set
            {
                if ((this._Title != value))
                {
                    this.OnTitleChanging(value);
                    this.SendPropertyChanging();
                    this._Title = value;
                    this.SendPropertyChanged("Title");
                    this.OnTitleChanged();
                }
            }
        }

Just a couple of comments before moving on to using this stuff from F#.

  • The class is marked as partial. This allows us to create another source file in the same project containing more fields, methods and properties for this class. Its perfect for code generators, because it allows clean separation between generated and hand-written code. F# does not seem to have such a feature, but I think it should.
  • The entity is by default a POCO. As with WCF, everything that LINQ to SQL needs is added via attributes.
  • The setter shown here looks busy, but the OnXXX methods are partial methods. If these are not implemented in another partial part of this class, the compile drops the calls. Again, a nice mechanism targeted at code generators, but missing from F#.

This code needs to be compiled up into an assembly, and referenced from the F# project.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

[Update: This blog post was orignally posted in early 2008, in another blog. Things have changed a little in F#, and Ted Nedward has provided updated information related to this, showing how to create data contracts in F#]

On the road to a really interesting Web Service, I increased the complexity of my "could-not-be-simpler" web service one notch, and added a class-typed argument to the operation. This requires constructing of a Data Contract, so that WCF can build the correct WSDL, and also know how to serialize (and de-serialize) messages. As with the Service Contract, this is simply done with a couple of attributes, like this:

[<DataContract>]
type public SimpleDataContract() =
        let mutable _propertyOne = System.String.Empty
        let mutable _propetyTwo = 0
        [<DataMember>]
        member public x.PropertyOne
            with get() = _propertyOne
            and set(value) = _propertyOne <- value           
        [<DataMember>]
        member public x.PropertyTwo
            with get() = _propetyTwo
            and set(value) = _propetyTwo <- value

[<ServiceContract(ConfigurationName = "ISimpleService", Namespace = "http://coyote-software.com/FSWCF/SimpleService")>]
type ISimpleService =
    [<OperationContract>]
    abstract TestMethod: Param:SimpleDataContract -> string

[<ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)>]
type SimpleService() =
    interface ISimpleService with
        member x.TestMethod s = "Hello " + s.PropertyOne;

Unfortunately, code causes a run-time exception when the service is started (well before any messages arrive). The error is about "method get_PropertyOne" not being a property, and therefore an invalid recipient of the DataMember attribute. I strongly suspect that this is due to a know bug in the current F# compiler emitting IL for properties in a non-standard way.

(WCF data contract members must be placed on properties, and not fields.)

The obvious work-around is to create the class in C#. The equivalent C# class is simply:

    [DataContract]
    public class SimpleDataContract
    {
        public SimpleDataContract() { }

        [DataMember]
        public string PropertyOne { get; set; }
        [DataMember]
        public int PropertyTwo { get; set; }
    }

  

For once, C# trumps F# for brevity! Anyway, using this class from F# is no more complex than referencing the dll, and removing the F# SimpleDataContract class, and the web service now works as expected.

The next step is to do something a little more interesting in the operation; use LINQ to SQL to query a database, and return some data. As it turns out, our data contracts will be again come via C#, so this issue will be by-passed.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati