Thursday, 27 March 2008

[important: this post belongs to a whole host of other posts. Basically it's about my efforts of building a light-weight n-tier disconnected solution for Entity Framework]

Another status update. I've worked on connecting up a clean graph to a EntityFramework context and found it far from straight-forward! I ended up with very little code though, which is a good sign.

The approach of not serializing the object context, makes it harder to figure out when an object was added or removed. On the client, I keep copies of the 'original' collection and I compare the new collection with the old collection. Problem there is that it's quite possible that an object was just removed from a collection and added to another collection. How do we know we have to do a real 'delete' on it?
I have not yet figured this out ;-)

Here is some code for you to look at:

  0    Customer c = new Customer { Name = "Ruurd Boeke" };
  1    Car car1 = new Car { Make = "Saab", Customer = c };
  2    Order order1 = new Order { Amount = 2, Customer = c };
  3
  4
  5    IEditableBusinessObject eC = c as IEditableBusinessObject;
  6    string MsgOnWire = "";
  7
  8    // at this point, we are not at all attached
  9    using (SimpleRelationshipTestEntities context = new SimpleRelationshipTestEntities())
  10    {
  11     context.AddToCustomer(c);
  12     context.SaveChanges();  // this instructs context to start changetracking
  13
  14     ((IContextAware)c).CreateSerializableState(context);
  15
  16     DataContractSerializer s = new DataContractSerializer(c.GetType(), null, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
  17
  18     MsgOnWire = s.GetWellFormedToContract(c);
  19    }
  20

The message on the wire looks like this:

<CustomerSurrogate xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="myNamespace">
    <Cars>
        <Car>
            <CarID>244</CarID>
            <Customer>
                <SerializationID>0</SerializationID>
            </Customer>
            <Make>Saab</Make>
            <SerializationID>1</SerializationID>
            <OriginalValue_CarID>244</OriginalValue_CarID>
            <OriginalValue_Customer>
                <SerializationID>0</SerializationID>
            </OriginalValue_Customer>
            <OriginalValue_Make>Saab</OriginalValue_Make>
        </Car>
    </Cars>
    <CustomerID>152</CustomerID>
    <Name>Ruurd Boeke</Name>
    <Orders>
        <Order>
            <Amount>2</Amount>
            <Customer>
                <SerializationID>0</SerializationID>
            </Customer>
            <OrderID>159</OrderID>
            <SerializationID>2</SerializationID>
            <OriginalValue_Amount>2</OriginalValue_Amount>
            <OriginalValue_Customer>
                <SerializationID>0</SerializationID>
            </OriginalValue_Customer>
            <OriginalValue_OrderID>159</OriginalValue_OrderID>
        </Order>
    </Orders>
    <SerializationID>0</SerializationID>
    <OriginalValue_Cars>
        <Car>
            <SerializationID>1</SerializationID>
        </Car>
    </OriginalValue_Cars>
    <OriginalValue_CustomerID>152</OriginalValue_CustomerID>
    <OriginalValue_Name>Ruurd Boeke</OriginalValue_Name>
    <OriginalValue_Orders>
        <Order>
            <SerializationID>2</SerializationID>
        </Order>
    </OriginalValue_Orders>
</CustomerSurrogate>

Next step is to receive this xml on the client, deserialize it and change some stuff:

  0    DataContractSerializer s2 = new DataContractSerializer(typeof(CustomerClient), null, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
  1    CustomerClient c2 = (CustomerClient)s2.UnwrapFromString(MsgOnWire);
  2
  3    // delete
  4    c2.Cars.First().Customer = null;
  5    c2.Cars.Clear();
  6
  7    // add
  8    OrderClient order2 = new OrderClient { Amount = 100, Customer = c2 };
  9    c2.Orders.Add(order2);
  10
  11    // change
  12    c2.Name = "Changed";
  13
  14    // we have edited everything, let's get back to the server
  15    DataContractSerializer s3 = new DataContractSerializer(typeof(CustomerClient), null, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
  16    MsgOnWire = s3.GetWellFormedToContract(c2);

This client is removing the cars. Note that we do not get the automatic hookup of relations that we used to get on the server: there is no EF Relationship manager on the client!

<CustomerSurrogate xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="myNamespace">
    <Cars />
    <CustomerID>152</CustomerID>
    <Name>Changed</Name>
    <Orders>
        <Order>
            <Amount>2</Amount>
            <Customer>
                <SerializationID>0</SerializationID>
            </Customer>
            <OrderID>159</OrderID>
            <SerializationID>1</SerializationID>
            <OriginalValue_Amount>2</OriginalValue_Amount>
            <OriginalValue_Customer>
                <SerializationID>0</SerializationID>
            </OriginalValue_Customer>
            <OriginalValue_OrderID>159</OriginalValue_OrderID>
        </Order>
        <Order>
            <Amount>100</Amount>
            <Customer>
                <SerializationID>0</SerializationID>
            </Customer>
            <SerializationID>2</SerializationID>
        </Order>
    </Orders>
    <SerializationID>0</SerializationID>
    <OriginalValue_Cars>
        <Car>
            <CarID>244</CarID>
            <Make>Saab</Make>
            <SerializationID>3</SerializationID>
            <OriginalValue_CarID>244</OriginalValue_CarID>
            <OriginalValue_Customer>
                <SerializationID>0</SerializationID>
            </OriginalValue_Customer>
            <OriginalValue_Make>Saab</OriginalValue_Make>
        </Car>
    </OriginalValue_Cars>
    <OriginalValue_CustomerID>152</OriginalValue_CustomerID>
    <OriginalValue_Name>Ruurd Boeke</OriginalValue_Name>
    <OriginalValue_Orders>
        <Order>
            <SerializationID>1</SerializationID>
        </Order>
    </OriginalValue_Orders>
</CustomerSurrogate>

We see that there are no current cars, but the original value of cars still has them. That is how I figure out to delete the cars on the server.
Did you also notice how the new order does not have a ID assigned? The server will take care of it.

  0    // we're at the server
  1    using (SimpleRelationshipTestEntities context = new SimpleRelationshipTestEntities())
  2    {
  3     DataContractSerializer s4 = new DataContractSerializer(typeof(Customer), null, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
  4     Customer c3 = (Customer)s4.UnwrapFromString(MsgOnWire);
  5
  6     ((IContextAware)c3).AttachGraphToContext(context,
  7      delegate(object source)
  8      {
  9       return source.GetType().Name;   // our setnames correspond to our classname.
  10      });
  11
  12
  13     context.SaveChanges();
  14    }

Now, we're deserializing and attaching the graph to the context. The delete on line 7 is a way of returning entitysetnames. EF needs to know the entitysetname to be able to attach an object to a context, but there is no way for me to know it. So at this point I let you make the decision yourself.

The database is correctly filled and we are happy.

Now, I really want to check in this source, but there is a lot to be done still. The API I have created is monstrous.
Also, I think there are quite a few situations where this will break horribly.

Once I get just a little happier about it, I will do a screencast building a server using Entity Framework and a silverlight client that is editing.

Thursday, 27 March 2008 17:20:28 (Romance Standard Time, UTC+01:00)  #    Comments [10]  |  Trackback
 Monday, 24 March 2008

Found through Julie's blog: a new tool was released on Code Gallery from the EF Team, called EF Mapping Helper.

It will help you play around with mappings and see what the result would be in the corresponding CSDL, SSDL and MSL generated xml.

Read more on Julie's blog and quickly download the tool!

Monday, 24 March 2008 17:08:14 (Romance Standard Time, UTC+01:00)  #    Comments [12]  |  Trackback
 Friday, 21 March 2008

I'm working hard on bringing a couple of my projects together that will enable domainobjects on the client without Entity Framework references. I thought I'd give a small update on how that's working out for me.

The endresult

What I want is one visual studio project: 'Domain' that holds business objects for me that I can use on both the client and the server. What I do not want, is to have to think about EntityFramework contexts on the client. Since I do not persist my objects on the client, I see no reason to actually create a context on the client, just to be able to do changetracking. Nor do I want to see any EF specific plumbing in the messages between that client and server: I lose interoperability if I do that. Also, if my client is Silverlight (for instance) I might not want to bring the EF assemblies over (if that's even possible?).

So, the endresult should be an easy way to serialize and deserialize objects on the client and server, and a way to build up the object correctly on the server so it can be attached to the context again.

Please check Daniel's EntityBag sample for a very sophisticated way of serializing your context. He does go down the route of serializing the objectcontext, which does mean you need the entityframework on the client. Read about his motivations for doing this here.

Let's see it

I'll first show some code and then walk you through it.

  0    using (OneSimpleType.OneSimpleTypeConnection context = new OneSimpleType.OneSimpleTypeConnection())
  1    {
  2     // clear out database
  3     foreach (OneSimpleType.Person old in context.Person)
  4     {
  5      context.DeleteObject(old);
  6     }
  7     context.SaveChanges();
  8    }
  9
  10    OneSimpleType.Person p = new OneSimpleType.Person { Firstname = "Ruurd", Lastname = "Boeke" };
  11    IEditableBusinessObject ep = p as IEditableBusinessObject;
  12    string MsgOnWire = "";
  13
  14    using (OneSimpleType.OneSimpleTypeConnection context = new OneSimpleType.OneSimpleTypeConnection())
  15    {
  16     context.AddToPerson(p);
  17     context.SaveChanges();  // at this point, there should be 'original values'
  18
  19     // the server is changing the object without yet saving
  20     p.Firstname = "ServerChanged";
  21
  22     // now we need to create a version of the object that can be serialized well
  23     p = (OneSimpleType.Person)context.CreateSerializableVersion(p);
  24
  25     DataContractSerializer s = new DataContractSerializer(p.GetType(), null, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
  26
  27     MsgOnWire = GetWellFormedToContract(p, s);
  28
  29    }
  30
  31    // imagine we are the client getting a message and deserializing it
  32    DataContractSerializer s2 = new DataContractSerializer(typeof(OneSimpleType.Person), null, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
  33    StringReader reader = new StringReader(MsgOnWire);
  34    XmlReader xreader = XmlReader.Create(reader);
  35    OneSimpleType.Person p2 = (OneSimpleType.Person)s2.ReadObject(xreader);
  36
  37    // edit something on the client
  38    p2.Lastname = "ClientChanged";
  39
  40
  41    // we have edited everything, let's get back to the server
  42    s2 = new DataContractSerializer(typeof(OneSimpleType.Person), null, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
  43    MsgOnWire = GetWellFormedToContract(p2, s2);
  44
  45    // we're at the server
  46    using (OneSimpleType.OneSimpleTypeConnection context = new OneSimpleType.OneSimpleTypeConnection())
  47    {
  48     DataContractSerializer s3 = new DataContractSerializer(typeof(OneSimpleType.Person), null, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
  49     StringReader reader3 = new StringReader(MsgOnWire);
  50     XmlReader xreader3 = XmlReader.Create(reader3);
  51     OneSimpleType.Person p3 = (OneSimpleType.Person)s3.ReadObject(xreader3);
  52
  53     context.AttachedDeserializedVersion(p3, "Person");
  54
  55     context.SaveChanges();
  56    }
  57

We start out on the serverside.
On line 17, we have just saved an object to the database. 
We continue using that (attached, and thus changetracked) object and change a property (just for the fun of it).
On line 27 we serialize it like you've seen me do in previous posts.

On line 32, we pretend to be on the client, which uses the message to deserialize to a compatible type. Note, that this type does not need to be 'IPoco' enhanced. It does need to be enhanced for serialization and editing.

We change a property 'on the client' on line 38. Notice that this object does not have a context attached at all.
At line 43 we have once again serialized this new object and on line 46 we pretend to be on the server again.

There we create a new context and deserialize our message to an object. Then we call an extension method of context to attach this version to the context. Note that I have to pass the setname (working on somehow getting to this information differently).
Finally we save.

Only the changed properties are flagged as dirty, so efficient SQL can be generated by EF.

The extension method is shown here:

  0   public static void AttachedDeserializedVersion(this ObjectContext context, object source, string setname)
  1   {
  2    // TODO: how to get setname from metadata or something
  3
  4    Debug.Assert(source is IEntityWithKey);
  5    Debug.Assert(source is IEditableBusinessObject);
  6
  7    IEditableBusinessObject eS = (IEditableBusinessObject)source;
  8
  9    // we go to the loaded state
  10    eS.SetReadMode(ReadMode.Loaded);
  11
  14    string fullEntitySetName = context.DefaultContainerName + "." + setname;
  15
  16    EntityKey createdKey = context.GetEntityKey(fullEntitySetName, source);
  17    ((IEntityWithKey)source).EntityKey = createdKey;
  18
  19    // attach this version
  20    context.Attach((IEntityWithKey)source);
  21
  22    eS.ReplayLoadedToNormal();
  23
  24    eS.SetReadMode(ReadMode.Normal);
  25
  26
  27   }

You see I have quite a bit of functionality in the IEditableBusinessObject interface.

The deserialized version of the object has a 'loaded' state (or better: original state) and a 'current' state. Before attaching at line 20, I first set my object to be in 'original' mode. Then I have a replay method that will compare the current values to the loaded versions and touch the setters of the properties of the changed versions. This notifies the changetracker of EF to flag a property as dirty.

This all works fine, but both code and api are pretty crude. I have no complextype or relationshiptype support, but that will come, if I don't run into major problems along the way.

I hope you are as excited about this as I am. Let me know!

Friday, 21 March 2008 18:21:28 (Romance Standard Time, UTC+01:00)  #    Comments [2]  |  Trackback
 Monday, 17 March 2008

I'll probably dedicate a bigger post to this soon, but I wanted to show you a domainmodel, some code and the xml it generates.
What you see here is two attributes that take on quite a bit of work.
EditableBusinessObject implements IEditableObject for you and also allows you to copy the currentvalues to a loadedvalues state.
CreateSerializeSurrogate generates a surrogate class that knows how to deal with loadedvalues and with circular references.
Together, they form the backbone of the client-side of your domainmodel.

Here is an example.

DomainModel:
(notice this is the full class, no other properties are here)

  0 [EditableBusinessObject]
  1 [CreateSerializeSurrogate]
  2 [Serializable]
  3 [DataContract(Namespace = "myNamespace", Name = "Person")]
  4  public class Person
  5 {
  6   [DataMember]
  7   public int IntProperty { get; set; }
  8
  9   [DataMember]
  10   public string StringProperty { get; set; }
  11
  12   [DataMember]
  13   public string StringProperty2 { get; set; }
  14
  15   int neverSetInt;
  16   [DataMember]
  17   public int NeverSetInt
  18   {
  19    get
  20    {
  21     return neverSetInt;
  22    }
  23    set { neverSetInt = value; }
  24   }
  25
  26   [DataMember]
  27   public List<string> StringLijst { get; set; }
  28
  29   [DataMember]
  30   public List<int> IntLijst { get; set; }
  31
  32   public Person()
  33   {
  34    StringLijst = new List<string>();
  35    IntLijst = new List<int>();
  36   }
  37
  38 }
  39

Some testcode:
(notice line 16 where we do a endEdit. If we had cancelled, we would've had a proper rollback)

  0    Person p = new Person();
  1    p.IntProperty = 1;
  2    p.StringProperty = "Ruurd";
  3    p.StringProperty2 = "Boeke";
  4
  5    p.StringLijst.Add("a");
  6    p.IntLijst.Add(1);
  7
  8    (p as IEditableBusinessObject).CopyCurrentToLoaded();
  9
  10    (p as IEditableBusinessObject).BeginEdit();
  11
  12    p.IntLijst.Add(2);
  13    p.StringLijst.Add("b");
  14    p.StringProperty = "Ruurd Boeke";
  15
  16    (p as IEditableBusinessObject).EndEdit();
  17
  18    DataContractSerializer s = new DataContractSerializer(p.GetType(), null, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
  19
  20    string outMessage = GetWellFormedToContract(p, s);

And the generated xml:
(notice how the lists and 'StringProperty' are changed, and see the original value).

<PersonSurrogate xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="myNamespace">
    <IntLijst xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
        <d2p1:int>1</d2p1:int>
        <d2p1:int>2</d2p1:int>
    </IntLijst>
    <IntProperty>1</IntProperty>
    <SerializationID>0</SerializationID>
    <StringLijst xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
        <d2p1:string>a</d2p1:string>
        <d2p1:string>b</d2p1:string>
    </StringLijst>
    <StringProperty>Ruurd Boeke</StringProperty>
    <StringProperty2>Boeke</StringProperty2>
    <OriginalValue_IntLijst xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
        <d2p1:int>1</d2p1:int>
    </OriginalValue_IntLijst>
    <OriginalValue_IntProperty>1</OriginalValue_IntProperty>
    <OriginalValue_StringLijst xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
        <d2p1:string>a</d2p1:string>
    </OriginalValue_StringLijst>
    <OriginalValue_StringProperty>Ruurd</OriginalValue_StringProperty>
    <OriginalValue_StringProperty2>Boeke</OriginalValue_StringProperty2>
</PersonSurrogate>

What is left, is deserializing, maybe propagating the beginedit commands to all children and then create the serverside EF variant.
Oh, and I don't like the originalValue representation. Maybe I'll generate a OriginalValue class to keep it tidy.

Monday, 17 March 2008 22:28:31 (Romance Standard Time, UTC+01:00)  #    Comments [11]  |  Trackback
 Sunday, 16 March 2008

[update: I've updated my project quite a bit, and moved the state to another object. So, the code you are about to see is old!]

In the grand scheme of things, I'm building toward a set of tools that will enable me to use EntityFramework on the server and use the same businessobjects on the client-side, without having to reference EntityFramework at all.
We already have a great domain layer supporting EF, without baseclasses. To use these objects on the client, we will need to take care of changetracking ourselves. When we are done with that, we'll just have to write a custom serializer, that will know how to serialize xml representations of the objects on the client-side, and deserialize on the server and vice versa.

I'll follow up in some post that describes how you can create different versions of your domain layer for use on either client-side or server-side.

The end result

Let's skip to the end result straight away. This is a little test that runs fine:

  0    Person p = new Person();
  1    p.Name = "Ruurd";
  2    p.MyProperty = 10;
  3    p.orders.Add("234");
  4    p.orders.Add("23asdf4");
  5    p.orders.Add("234345zs");
  6
  7
  8    IEditableBusinessObject editablePerson = p as IEditableBusinessObject;
  9
  10    editablePerson.CopyCurrentToLoaded();
  11
  12    editablePerson.BeginEdit();
  13
  14    p.Name = "Boeke";
  15    p.orders.Add("zxcvzxcvzxcvzxcv");
  16
  17    Assert.IsTrue(p.Name == "Boeke");
  18    Assert.IsTrue(p.orders.Count == 4);
  19
  20    editablePerson.CancelEdit();
  21
  22    Assert.IsTrue(p.orders.Count == 3);
  23    Assert.IsTrue(p.Name == "Ruurd");

Just so we are on the same page, we did not have to implement anything on the business object itself. Only use an attribute: the EditableBusinessObjectAttribute. It will take care of everything for ya.

  0 [EditableBusinessObject]
  1 [Serializable]
  2  public class Person
  3 {
  4   public int MyProperty { get; set; }
  5
  6   public string Name { get; set; }
  7
  8   public List<string> orders { get; set; }
  9
  10   public Person()
  11   {
  12    orders = new List<string>();
  13   }
  14
  15 }

Editable business objects

There is already a great usersample available on how to use PostSharp to implement this interface. I took a different route, but you should still check out the sample, because it is actually quite well put together.

My scenario has the following requirements:

  • implement IEditableObject (obviously)
  • Have a way to copy values to a 'Loaded' state: necessary to be able to send the 'original values' to Entity Framework
  • Retrieve that loaded state: will be used by the serializer, to actually create the original value serialization

So, I created the following interface that we will implement using PostSharp:

  0  public interface IEditableBusinessObject : IEditableObject
  1 {
  2   void RegisterFieldAccessAspect(FieldInterceptionAspect aspect);
  3
  4   void CopyCurrentToLoaded();
  5
  6   object RetrieveLoadedState(string FieldName);
  7
  8   bool HasPendingTransaction();
  9 }
  10

To implement, we use the familiar CompositionAspect.

For the real magic, we need to intercept all gets and sets of all fields in the business object.

Intercept field access

A very strong feature of PostSharp is the OnFieldAccessAspect. It allows you to actually intercept field access and route it to something different.
The implementation of IEditableBusinessObject knows when the businessobject is set to beginedit and should notify all fieldaspects. We do this by 'registering' the fieldaspect at the editableBusinessObject. This way, when our object is going to beginEdit mode, all we have to do is iterate our fieldaspects and let them know.

At that point the fieldaspect makes a copy of it's value to the corresponding statebag. If our value was not a value type, we make a deepcopy. I'm contemplating adding support for ICloneable, so we can possibly skip that step.

So, the begin, cancel and endedit methods on the fieldaspect look something like this:

  0   internal void BeginEdit()
  1   {
  2    if (needsBinaryCopy)
  3    {
  4     MemoryStream m = new MemoryStream();
  5     BinaryFormatter b = new BinaryFormatter();
  6     b.Serialize(m, CurrentState);
  7     m.Position = 0;
  8     PendingState = b.Deserialize(m);
  9    }
  10    else
  11    {
  12     PendingState = CurrentState;
  13    }
  14   } 
  15  
  16   internal void CancelEdit()
  17   {
  18    PendingState = null;
  19   }
  20
  21   internal void EndEdit()
  22   {
  23    CurrentState = PendingState;
  24    PendingState = null;
  25   }

And the get and set methods of the field look like this:

  0   public override void OnGetValue(FieldAccessEventArgs eventArgs)
  1   {
  2    IEditableBusinessObject editableObjectImpl = (IEditableBusinessObject)eventArgs.Instance;
  3    editableObjectImpl.RegisterFieldAccessAspect(this);
  4
  5    eventArgs.ExposedFieldValue = editableObjectImpl.HasPendingTransaction() ? PendingState : CurrentState;
  6   }
  7
  8   public override void OnSetValue(FieldAccessEventArgs eventArgs)
  9   {
  10    IEditableBusinessObject editableObjectImpl = (IEditableBusinessObject)eventArgs.Instance;
  11    editableObjectImpl.RegisterFieldAccessAspect(this);
  12
  13
  14
  15    if (editableObjectImpl.HasPendingTransaction())
  16    {
  17     PendingState = eventArgs.ExposedFieldValue;
  18    }
  19    else
  20    {
  21     CurrentState = eventArgs.ExposedFieldValue;
  22    }
  23   }

As you can see, it checks with the instance, whether we have a pending transaction (we have started beginedit). If we do, we write to the pendingstate object, instead of the current state.

I don't feel too comfortable with the code at this point, because I took quite a bit of shortcuts. So I'm not putting that online at this point. However, if you are really interested, let me know in the comments, and I'll clean it up.

Stay tuned, because the next step will be to get this to serialize nicely, with originalvalues and such.

Sunday, 16 March 2008 15:16:14 (Romance Standard Time, UTC+01:00)  #    Comments [2]  |  Trackback
 Friday, 14 March 2008

This is so not agile.

I have a namespace: EntityFrameworkContrib.PostSharp4EF.Testing.ComplexType and a testclass: ComplexTypeTest.
When I call to EntityFramework it apparently wants to log something and fails because of a 127 category name limit: Instance names used for writing to custom counters must be 127 characters or less.

This is shown in the callstack:

System.dll!System.Diagnostics.SharedPerformanceCounter.SharedPerformanceCounter(string catName, string counterName = "numberofactiveconnectionpoolgroups", string instanceName = "unittestadapterdomain_forg:_dev_efcontrib_entityframeworkcontrib.postsharp4ef_testresults_shrtrun[18]_out_entityframeworkcontrib.postsharp4ef.testing.dll[12116]", System.Diagnostics.PerformanceCounterInstanceLifetime lifetime = Process) + 0xcc bytes   

You can see I've already made a 'shrtrun' name for the tests, instead of the long generated name. That was suggested on a few forums: the name generated by the testframework is very long and can be shortened. That might help you:

Go to Test/Edit Testrun configuration
image

However, no way does that help enough on my end.
I believe I read that this was a bug that they would fix in 2008, but it's still there.

Does that mean I should shorten my namespaces? I don't really feel like it, since I believe in clear names. One of the contributors of EFContrib even suggested writing contrib as contribution!

My current workaround is a console application that tests the code that I'm working on, but I need to build out tests now.

Anybody know any real workarounds?

Friday, 14 March 2008 18:25:22 (Romance Standard Time, UTC+01:00)  #    Comments [10]  |  Trackback

I just finished full support for using complex types in EFContrib. I thought I'd quickly share what complex types are and how they are used in Entity Framework.

Julie Lerman blogged about Complex Types here, where she also shows how to use them. Check her post for a great example.
In Daniel Simmons' words:
Complex types “Complex types” is the Entity Framework name for value properties which have more intricate structure than scalars. The canonical example is an Address type which contains several parts (street, city, state, etc.) Complex types are somewhat like entities except that they do not have any identity of their own (they are value types). This means that a complex type instance is always a part of some other enclosing entity—it can’t stand on its own, it doesn’t have relationships, etc. In this release, the mapping scenarios for complex types are significantly limited: inheritance is not supported, complex type properties cannot be null and they can only occur in single instances, not collections.

So, a complex type can be seen as a struct, without identity.
Let's create a complex type. I will have a person table with 3 address related columns. My person object though, should have a property named 'Address' which points to a class of type Address.
The CSDL looks like this:

        <EntityType Name="Person">
          <Key>
            <PropertyRef Name="PersonID" />
          </Key>
          <Property Name="PersonID" Type="Int32" Nullable="false" />
          <Property Name="FirstName" Type="String" Nullable="false" MaxLength="50" Unicode="false" />
          <Property Name="LastName" Type="String" Nullable="false" MaxLength="50" Unicode="false" />
          <Property Name="Address" Type="Self.Address" Nullable="false" />
        </EntityType>
        <ComplexType Name="Address">
          <Property Name="City" Type="String" Nullable="false" MaxLength="50" Unicode="false" />
          <Property Name="Street" Type="String" Nullable="false" MaxLength="50" Unicode="false" />
          <Property Name="PostalCode" Type="String" Nullable="false" MaxLength="10" Unicode="false" />
</ComplexType>

Pretty clear: our Person has an address property, with a type of 'Self.Address'. Make sure it is not Nullable. The Address is defined just like you would expect. Please know that the current designer does not allow designing complextypes visually, which is why I did it in xml.

The database looks like this:

        <EntityContainer Name="dbo">
          <EntitySet Name="Person" EntityType="ComplexTypesTestModel.Store.Person" />
        </EntityContainer>
        <EntityType Name="Person">
          <Key>
            <PropertyRef Name="PersonID" />
          </Key>
          <Property Name="PersonID" Type="int" Nullable="false"  StoreGeneratedPattern="Identity" />
          <Property Name="FirstName" Type="varchar" Nullable="false" MaxLength="50" />
          <Property Name="LastName" Type="varchar" Nullable="false" MaxLength="50" />
          <Property Name="Street" Type="varchar" Nullable="false" MaxLength="50" />
          <Property Name="City" Type="varchar" Nullable="false" MaxLength="50" />
          <Property Name="PostalCode" Type="varchar" Nullable="false" MaxLength="10" />
</EntityType>

Here we see a row with all columns in it. Darned DBA's!!

To map that database description to the classes, we take a look at the C-S mapping:

          <EntitySetMapping Name="Person">
            <EntityTypeMapping TypeName="IsTypeOf(EntityFrameworkContrib.PostSharp4EF.Testing.ComplexType.Person)">
              <MappingFragment StoreEntitySet="Person">
                <ScalarProperty Name="PersonID" ColumnName="PersonID" />
                <ScalarProperty Name="FirstName" ColumnName="FirstName" />
                <ScalarProperty Name="LastName" ColumnName="LastName" />
                <ComplexProperty Name="Address">
                  <ScalarProperty Name="City" ColumnName="City"/>
                  <ScalarProperty Name="Street" ColumnName="Street"/>
                  <ScalarProperty Name="PostalCode" ColumnName="PostalCode"/>
                </ComplexProperty>
              </MappingFragment>
            </EntityTypeMapping>
</EntitySetMapping>

As you can see, the complexproperty is defined within the Person mapping. That caught me off-guard for a while.

EFContrib support

I have not checked in the source yet, but I will shortly at http://www.codeplex.com/efcontrib.
As you know, my contribution project to Entity Framework aims to help you use Entity Framework without all the generated code. You can just create your own domain model and add one attribute. The system will actually change the code to facilitate the EDM during compilation. Leaving you with a clean model.

It was actually quite difficult to implement this behind the scenes. To support normal entitytypes, I have to implement the three IPoco interfaces. But complex types are radically different. In the end, I had to alter the code I put into the setters of your properties.
I also had to somehow get hold of a list of properties in your type that are complexTypes (in this case Address). When the system injects your entitytype with a changetracker, it should notify all complex types. I could have done that with reflection, but we all know that's really slow. So I actually generate a method in your entity: 'UpdateComplexTypes(tracker)' and insert that with the correct IL to set the tracker in all the complextype-properties. So the solution is as fast as it can get, completely on par with handwritten c#. I may have to write another post on how I did it.

Our domain objects look like this:

  0 [Poco("ComplexTypesTestEntities")]
  1  public class Person
  2 {
  3   public int PersonID { get; set; }
  4   public string FirstName { get; set; }
  5   public string LastName { get; set; }
  6   public Address Address { get; set; }
  7 }
  8
  9 [Poco("ComplexTypesTestEntities")]
  10  public class Address
  11 {
  12   public string City { get; set; }
  13   public string Street { get; set; }
  14   public string PostalCode { get; set; }
  15 }
  16

Now, this code will work great:

  0    using (ComplexTypesTestEntities context = new ComplexTypesTestEntities())
  1    {
  2     // clear out database
  3     foreach (Person old in context.Person)
  4     {
  5      context.DeleteObject(old);
  6     }
  7     context.SaveChanges();
  8
  9     Person p = new Person { FirstName = "Ruurd", LastName = "Boeke" };
  10
  11     // this will set the changetracker
  12     context.AddToPerson(p);
  13
  14     Address a = new Address { City = "Rotterdam", Street = "My Street", PostalCode = "1111 VA" };
  15     p.Address = a;
  16
  17     IGetChangeTracker ctA = PostSharp.Post.Cast<Address, IGetChangeTracker>(a);
  18     Debug.Assert(ctA.GetChangeTracker() != null);
  19
  20     Address b = new Address { City = "Seattle", Street = "redmond street", PostalCode = "2222 BB" };
  21     p.Address = b;
  22     Debug.Assert(ctA.GetChangeTracker() == null);
  23
  24     IGetChangeTracker ctB = PostSharp.Post.Cast<Address, IGetChangeTracker>(b);
  25     Debug.Assert(ctB.GetChangeTracker() != null);
  26
  27     context.SaveChanges();
  28    }

You can see me casting the object to the interface I implemented on lines 17 and 24.

Friday, 14 March 2008 17:36:55 (Romance Standard Time, UTC+01:00)  #    Comments [7]  |  Trackback
 Thursday, 13 March 2008

I have not taken the time to actually play with SL yet. But I have been watching the mix sessions and are very excited about what I see. However, from what I can gather, there are a few things missing. It seems important to get this news out soon, so Microsoft can start getting some customer feedback.

For starters, there is no combobox, but I must say that this really isn't a big problem. It might even be included in the final bits, or somebody will write one for sure. No biggy.

However, Karen's video highlights some shortcomings of styling: they are write once, no application theme style like we are used to and you can not use 'based on'. That is going to be awful to say the least!

Josh Twist has an excellent post up here, where he notices the missing elementname and relativesource bindings. He doesn't use the relativesource bindings much, but I can tell you, I've had to use them to enable some great scenario's. Not having them will limit me greatly (more then missing the elementname binding).

Finally, Rob Eisenberg triggered me to write this post, mainly to point to his post here. It's a great writeup of what he is missing. It appears there are not triggers around anymore. I had already noticed that SL is coming up with a new scheme for control template authors to define the 'states' and 'elements' that need to be implemented by a template author. However great that may be, they appear to have substituted this for the trigger functionality. I can tell you, that is just terrible. Triggers are a very important part of a wpf application, and like Rob, I would implore Microsoft to reconsider this.
He also notes there is no concept of the ambient datacontext anymore. wow.. that hurts. No longer parameter-less ctors for UIElement and Frameworkelement (so, you can only inherit from Control easily) and to me one of the most important features missing: no Commands!! That means we are in a world of pain.

I must say, this has diminished my expectations of Silverlight a little bit. I really hope some of these issues are fixed before going live, because they have proven so useful in WPF.

Thursday, 13 March 2008 10:07:17 (Romance Standard Time, UTC+01:00)  #    Comments [3]  |  Trackback
 Tuesday, 11 March 2008

The last couple of months have been great, working on stuff that I wanted to learn more about. Now it is time to start looking for new jobs again.

I'm a freelance software engineer based in Holland (Rotterdam). If you or your company is looking for help, you can hire me for a few days/weeks/months.
Since you're reading this blog, I'm sure you know that I love WPF, WF, WCF and EF alike, so I'm a pretty rounded in the new 3.0 technologies. Apart from jobs that work with those technologies, I'm also interested in jobs where I can coach a team of developers to work agile or where I can help design your next great architecture ;-)

So, if you need a hand, email me at: work at sitechno dot com.

Tuesday, 11 March 2008 18:02:57 (Romance Standard Time, UTC+01:00)  #    Comments [11]  |  Trackback

In a previous post, I presented a PostSharp weaver that created a surrogate class for all your domain classes to help in serializing circular references (you know: Father.Children and Child.Father will cause WCF to go into a loop). The important enabler there was a List<object> that I held onto while (de)serializing. I was unhappy with the way I had solved that, so in this post, I'll just fix that.

Part of the problem was that the Server (dispatcher) has a correctly initialized OperationContext, where I could get to OperationContext.Current and do stuff. Extensions that were added in an earlier phase were no longer accessible though. A shame really.
Nicholas Allen was able to tell me how I could get a OperationContext going in the client. You can just create an OperationContext. I created a proxy class (by hand) that does this. People who love generated code are out of luck on this one, but I would recommend against using the generation tools of VS for WCF anyway.

My Proxyclass:

  0  public class PersonSvcClient : ClientBase<IPersonService>, IPersonService
  1 {
  2   public PersonSvcClient() : base ("PersonEndpoint")
  3   {
  4
  5   }
  6   #region IPersonService Members
  7
  8   public void SendPersonGraphToService(Person person)
  9   {
  10    using (new OperationContextScope(this.InnerChannel))
  11    {
  12     base.Channel.SendPersonGraphToService(person);
  13    }
  14   }
  15
  16   public Person GetPersonToClient()
  17   {
  18    using (new OperationContextScope(this.InnerChannel))
  19    {
  20     return base.Channel.GetPersonToClient();
  21    }
  22   }
  23
  24
  25   #endregion
  26 }

As you can see, I'm putting the actual calls inside a using block that just creates an operationcontextscope.

Now, if I were to subscribe to the OperationContext.Current.OperationCompleted, you would think that I had a great place to clean up. However, this event does not get thrown on the client (it works correctly on the server..). Weird.

However, now I have an OperationContext.Current in both client as server, so I can add an extension to it. Beware, I add this extension right in the surrogate. This way, it will stay around during the serialization process.

  0   public class MyContext : IExtension<OperationContext>
  1   {
  2    public static MyContext GetCurrentContext()
  3    {
  4     MyContext c = OperationContext.Current.Extensions.Find<MyContext>();
  5     if (c == null)
  6     {
  7      OperationContext.Current.Extensions.Add(c = new MyContext());
  8     }
  9
  10     return c;
  11    }
  12
  13    /// <summary>
  14    /// will keep our objects during serialization. Need to clear this before serialization (operation behavior)
  15    /// </summary>
  16    public List<object> serializationList = new List<object>();
  17
  18    /// <summary>
  19    /// will keep our objects during desrialization. Need to clear this before deserialization (operation behavior)
  20    /// </summary>
  21    /// <remarks>the key is the serialization ID that is transmitted by the </remarks>
  22    public Dictionary<int, object> deserializationList = new Dictionary<int, object>();
  23
  24    #region IExtension<OperationContext> Members
  25
  26    public void Attach(OperationContext owner)
  27    {
  28    }
  29
  30    public void Detach(OperationContext owner)
  31    {
  32    }
  33
  34    #endregion
  35   }

I've added a static GetCurrentContext method, that will add the context class if it does not exist, and return it.

Finally, now I can be sure that I have a safe place to store my lists, and it get's garbage collected properly.

Tuesday, 11 March 2008 14:19:12 (Romance Standard Time, UTC+01:00)  #    Comments [12]  |  Trackback
 Monday, 10 March 2008

I've been working on the PostSharp4EF project of EFContrib. I'm pleased to announce relationships are now supported.

This actually proved to be somewhat more work, because of all the things that are happening behind the scenes.
Entity Framework expects EdmRelationshipNavigationProperty attributes on top of the property that points to a relationship. But there is also the giant EdmRelationshipAttribute with about 8 constructor parameters.
So, I now generate these for you.

The next step was to actually get the relationship for you. I do that by removing the backing field, and replacing it with methods that access the relationshipmanager to get the relationship.

I do not want you to work directly with EF types, but you will need a way to Load relationships. So, I've introduced an interface with the appropriate Load and IsLoaded methods. Just in case you do want to hook into the EntityCollection and EntityReference, I've also supplied methods on that interface that will get you these types.

Let's look at a domain model:

  0 [Poco("SimpleRelationshipTestEntities")]
  1  public class Customer
  2 {
  3   public int CustomerID { get; set; }
  4   public string Name { get; set; }
  5
  6   public ICollection<Car> Cars { get; set; }
  7
  8   public ICollection<Order> Orders { get; set; }
  9 }
  10
  11 [Poco("SimpleRelationshipTestEntities")]
  12  public class Car
  13 {
  14   public int CarID { get; set; }
  15   public string Make { get; set; }
  16
  17   public Customer Customer { get; set; }
  18
  19 }
  20
  21 [Poco("SimpleRelationshipTestEntities")]
  22  public class Order
  23 {
  24   public int OrderID { get; set; }
  25   public int Amount { get; set; }
  26
  27   public Customer Customer { get; set; }
  28
  29
  30 }
  31

You might notice that I do not expose the concrete EF types, but instead expose ICollections.
After compilation, you can use this domainmodel like this:

  0    using (SimpleRelationshipTestEntities context = new SimpleRelationshipTestEntities())
  1    {
  2
  3     foreach (Car oldCar in context.Car)
  4     {
  5      IRelationshipLoader noLazyLoading = PostSharp.Post.Cast<Car, IRelationshipLoader>(oldCar);
  6
  7      bool wasLoaded = noLazyLoading.IsLoaded("Customer");
  8
  9      EntityReference<Customer> cu = noLazyLoading.GetRelatedReference<Customer>("Customer");
  10      cu.Load();
  11
  12      wasLoaded = noLazyLoading.IsLoaded("Customer");
  13     }
  14
  15
  16
  17     // clear out database
  18     foreach (Customer old in context.Customer)
  19     {
  20      IRelationshipLoader noLazyLoading = PostSharp.Post.Cast<Customer, IRelationshipLoader>(old);
  21
  22      bool wasLoaded = noLazyLoading.IsLoaded("Orders");
  23
  24      noLazyLoading.Load("Orders");
  25      wasLoaded = noLazyLoading.IsLoaded("Orders");
  26
  27      EntityCollection<Order> orders = noLazyLoading.GetRelatedCollection<Order>("Orders");
  28
  29      context.DeleteObject(old);
  30     }
  31     context.SaveChanges();
  32
  33
  34     Customer c = new Customer { Name = "Ruurd Boeke" };
  35     Customer c2 = new Customer { Name = "Test Customer" };
  36     Car car = new Car { Make = "Ferrari" };
  37     Order o1 = new Order { Amount = 10 };
  38     Order o2 = new Order { Amount = 20 };
  39     Order o3 = new Order { Amount = 30 };
  40
  41     car.Customer = c;
  42     car.Customer = c2// can re assign test
  43
  44     // add customer on order
  45     o1.Customer = c;
  46     o2.Customer = c;
  47     // add order on customer, new way
  48     c.Orders.Add(o3);
  49
  50
  51
  52     context.AddToCustomer(c);
  53     context.AddToCustomer(c2);
  54     context.SaveChanges();
  55    }
  56   }

Points of interest:
Line 5 casts your object to the implemented interface. This is checked at compile-time, so no worries about breaking at runtime. Using this cast, I can get to the EF types like I do at line 9 and line 27. Obviously, if you were inclined, you could place these methods on your domain classes. It does couple you to EF more though and clutters the code.

Next up are complex types. When that's done, I'm finished.

Quite pleased with this actually! ;-)

Monday, 10 March 2008 17:56:40 (Romance Standard Time, UTC+01:00)  #    Comments [5]  |  Trackback
 Wednesday, 05 March 2008

Finally, very exciting!! Scott released it at Mix 08.

Go get it here.

It seems they have released an Expression Blend version to match. Cool!
Also 2000 thousand unittests are released.

Wednesday, 05 March 2008 21:33:26 (Romance Standard Time, UTC+01:00)  #    Comments [0]  |  Trackback

the Oredev-conference posted video's of it's sessions online here.

I've just browsed the sessions that were held at this big conference, and it looks interesting. Many famous names, like Erik Meijer, Joe Duffy, Jimmy Nilsson (I read his book), Mats Helander and many others.

The conference has specific tracks: Java, .Net, Methods & Tools, Test, Project Management, Embedded System, Architecture and User Experience.

So sit back, relax, have a beer and hear great people explain important topics to you.

Just a quick tip: you can only watch the video's inside a browser. If you try to download, you will get a very small file. Open that with notepad and look for the rts: address at the very end. Copy paste the url into a good media player like VLC which knows how to deal with streams, and you will be able to watch more comfortably. I did this, because I could not scale the webversion and I had no way of scrubbing through the video.

kick it on DotNetKicks.com

Wednesday, 05 March 2008 17:48:13 (Romance Standard Time, UTC+01:00)  #    Comments [0]  |  Trackback
 Tuesday, 04 March 2008

Ever since I first got into workflow foundation, I've taken a fancy to statemachines. Once you wrap your head around them, they are a natural fit for most business processes.
The main problem everybody seems to be having with workflow though, is the versioning story. There is none!
That might be a bit harsh, you can certainly version your workflows, but to tell you the truth, you will be in a world of hurt.

The sample solution can be downloaded at the end of the post. It contains two workflows and a console application that you can play with.

Why is this updating so tough?
The workflow template is serialized to the persistence store. Any change in the workflow (adding or removing an activity) will make it impossible to deserialize the workflow again. It's serialized as a blob, so no easy transformation. I've written extensively about problems surrounding updating workflows here.

Your options pretty much exist of running side by side (which gives you a world of even more hurt, because now you have your data exchange services to version as well, and the activity library you have built) or use dynamic changes to alter the structure.
The latter being your best bet, but so much work that it takes away from the flexibility and speed of development that workflow brings to the table.

In my previous post I concluded that you would be best of just destroying your old workflow and create a new one. I stand by that! Today I was finally able to revisit the problem, and I hacked together a solution that might be interesting to people.

This solution has the following restriction:

It will only work for statemachines, that are waiting inside a state for an eventdriven activity, not inside an eventdriven activity. In other words: it is only able to update workflows that have entered a state and started waiting, not ones that have executed a few activities and is now waiting on some other input within a sequence.

Luckily for me, that is no problem at all, and it should not be a problem for you either. Statemachines should be modeled such that waiting happens when entered in a state, never inside a sequence. You can model waits inside a sequence, but I would suggest you make the delays short (minutes, as opposed to days/months/years).

My goal here is to be able to do a relatively easy update, where I have control over how I update (what to do with state etc.) and get my delays initialized to the correct timeouts again. So, in workflow1 I had a delay of 11 months, with 8 months left. When I start workflow2 and update, I need to have 8 months left again, and not 11.

Getting the delays right is the hard part.

I use some nice reflection to get to the actual type of a workflow instance. I described how to do that here. However, I was being silly. It's much easier:

            Workflow1 oldWF = workflowRuntime.GetRootActivity(instance) as Workflow1;

Made possible by these extensions:

    public static class WFExtensions
    {
        public static object GetExecutor(this WorkflowRuntime workflowRuntime, WorkflowInstance instance)
        {
            return workflowRuntime.GetType().InvokeMember(
                "Load", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, workflowRuntime,
                new object[] { instance.InstanceId, null, instance });
        }
        public static object GetRootActivity(this WorkflowRuntime workflowRuntime, WorkflowInstance instance)
        {
            object executor = workflowRuntime.GetExecutor(instance);
            return executor.GetType().GetField("rootActivity",
                    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField).GetValue(executor) as CompositeActivity;
        }
    }

So, here goes:

  1. Get to your old workflow instance. In my sample I use types Workflow1 and Workflow2.
                WorkflowInstance instance = runtime.GetWorkflow(g);
    
                WorkflowRuntime workflowRuntime = runtime;
    
                 
                Workflow1 oldWF = workflowRuntime.GetRootActivity(instance) as Workflow1;
    
                if (oldWF == null)
    
                    return;
    
                object executor = workflowRuntime.GetExecutor(instance);
    
                instance.Suspend("asdf");   // need not to unload, otherwise the database record would be unlocked

    I suspend the workflow, so it does not get into the way, but I can not unload, or worse: terminate. That would kill the record in the database.

  2. Create a new workflow, of your desired type, and copy the workflowInstanceID to it:
                // get a handle to the instanceid property
    
                DependencyProperty instanceidDP = (DependencyProperty)executor.GetType().GetField("WorkflowInstanceIdProperty",
    
                    BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).GetValue(executor);
    
                // create new wf2, not starting it yet
    
                WorkflowInstance newWFInstance = workflowRuntime.CreateWorkflow(typeof(Workflow2));
    
                Workflow2 newWF = workflowRuntime.GetRootActivity(newWFInstance) as Workflow2;
    
                // copy the guid
    
                newWF.SetValue(instanceidDP, instance.InstanceId);
  3. Build up a list of activities that are on timers and remember their name and when they expire:
                Dictionary<string, DateTime> activitiesExpireList = new Dictionary<string, DateTime>();
    
                TimerEventSubscriptionCollection subscriptions = ((TimerEventSubscriptionCollection)
    
                    oldWF.GetValue(TimerEventSubscriptionCollection.TimerCollectionProperty));
    
                foreach (TimerEventSubscription subscription in subscriptions)
    
                {
    
                    // find out what activity was subscribed
    
                    var x = from queueInfo in instance.GetWorkflowQueueData()
    
                            where subscription.QueueName.GetType().Equals(queueInfo.QueueName.GetType())
    
                            where subscription.QueueName.CompareTo(queueInfo.QueueName) == 0
    
                            select new { ExpiresAt = subscription.ExpiresAt, Activities = queueInfo.SubscribedActivityNames };
    
                    foreach (var combination in x)
    
                    {
    
                        foreach (string activityname in combination.Activities)
    
                        {
    
                            activitiesExpireList.Add(activityname, combination.ExpiresAt);
    
                        }
    
                    }
    
                }

    The weird part being the fact that the queue names are mostly guids (for delays atleast).

  4. Call a method on your new type. See how cool it is we can actually communicate this way with it, instead of having to go through communication services!!
                // allow new workflow to read information from old workflow to init itself.
    
                newWF.Update(oldWF, instance, activitiesExpireList);
  5. Copy the new workflow to the rootactivity of our executor. Ouch.. yeah.. don't worry.
                // copy the new rootactivity to the executor
    
                executor.GetType().GetField("rootActivity",
    
                    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField).SetValue(executor, newWF);
    
  6. Last bits:
                // start it up
    
                newWFInstance.Start();
    
                newWFInstance.Unload(); // overwrites current record in persistence store
    
                instance.Abort();   // kills of our original
    
                newWFInstance = runtime.GetWorkflow(g);
    
                StateMachineWorkflowInstance statemachine = new StateMachineWorkflowInstance(runtime, g);
    
                statemachine.SetState(newWF.DefineNewStateAfterUpdate(oldWF.CurrentStateName));
    
                // still need to unload or unload the runtime to get all timers correctly!
    
                Console.WriteLine("updated" + newWFInstance.InstanceId);

    You can see me starting and unloading, then killing our old instance. Finally I am trying to be smart by using the statemachineworkflowinstance to do a transition to a new state on the new workflow. The newstate can be determined by the new workflow (who has knowledge of these things) but is usually the same as in your old workflow. (This was build so that you could rename a state).

  7. That's it. In the Workflow2 class, I have an update method, which will set a boolean to true. The initialization activity will look for it in an if/else and not do anything if it is set to true. All the delays in the new workflow have an initTimeout method like so:
            private void initTimeout(object sender, EventArgs e)
    
            {
    
                DelayActivity delay = (DelayActivity)sender;
    
                if (activitiesExpireList.ContainsKey(delay.Name))
    
                {
    
                    delay.TimeoutDuration = activitiesExpireList[delay.Name].Subtract(DateTime.Now.ToUniversalTime());
    
                    activitiesExpireList.Remove(delay.Name);
    
                }
    
            }
     

I have uploaded the complete sample here.

When you run it, you can press 'c' to create a new workflow of type Workflow1. Then you can press 'u' and paste in the guid of the workflow just created. It will update the workflow. Pressing 'b' will break and unload the workflow.
Your created workflow has this state:

image

Where the delay is 40 seconds. Workflow2 has the same state, but has a delay of only 10 seconds.

As a test you can see that after updating, you will have a workflow2 running (there is another activity present that will print out debug information). The delay was set correctly.

Obviously, you might want to deal with the delays your own way. Because you have all the information in your workflow codebehind, you can think of your own rules on how the delay timeouts should be set.

Realize that touching the internals of WF like this is not what Microsoft envisioned and should be done with care.

Have fun, and let me know what you think.

kick it on DotNetKicks.com

Tuesday, 04 March 2008 23:56:35 (Romance Standard Time, UTC+01:00)  #    Comments [15]  |  Trackback
 Monday, 03 March 2008

Ever since I was using domain objects on the wire and using WCF httpbindings to serialize them, I've had one serious issue with the way cyclic / circular references are serialized. In this post I present a solution that will create a more beautiful XML representation of your object graph than ever before!
As you know, I'm a big proponent of easy Domain Driven Design and flexibility. So, the solution I present you will be supereasy to implement (just use an attribute).

The issue at hand

Let's create two objects that have references to each other:

    [DataContract(Namespace = "myNamespace", Name = "Person")]
    public class Person
    {
        [DataMember]
        public int PersonID { get; set; }
        [DataMember]
        public string FirstName { get; set; }
        [DataMember]
        public string LastName { get; set; }
        [DataMember]
        public int Something;
        [DataMember]
        public List<int> Numbers { get; set; }
        [DataMember]
        public List<int> FieldedNumbers;
        [DataMember]
        public List<Order> Orders { get; set; }
        public Person()
        {
            Orders = new List<Order>();
            Numbers = new List<int>();
            FieldedNumbers = new List<int>();
        }
    }
    [DataContract(Namespace = "myNamespace", Name = "Order")]
    public class Order
    {
        [DataMember]
        public Person Customer { get; set; }
        [DataMember]
        public int Amount { get; set; }
        [DataMember]
        public int ProductID { get; set; }
        [DataMember]
        public int OrderID { get; set; }
        }

The diagram thus looks like this:

image

When you use a regular DataContractSerializer to serialize this, you will get a stack overflow, since there is a circular reference between person and order.

  1         [TestMethod]
  2         public void TestWithoutSurrogateSubstitution()
  3         {
  4             Person p = new Person { PersonID = 23, FirstName = "Ruurd", LastName = "Boeke", Something = 666 };
  5             p.Numbers.Add(31);
  6             p.Numbers.Add(29);
  7 
  8             p.FieldedNumbers.Add(12);
  9             p.FieldedNumbers.Add(24);
 10 
 11             p.Orders.Add(new Order { Amount = 12, Customer = p, OrderID = 12, ProductID = 19 });
 12             p.Orders.Add(new Order { Amount = 12, Customer = p, OrderID = 13, ProductID = 255 });
 13 
 14             DataContractSerializer s = new DataContractSerializer(p.GetType(), null, int.MaxValue, false, true, null);
 15 
 16             string outMessage = GetWellFormedToContract(p, s);
17         }

So, on line 14, you will see that I have to instantiate my serializer with the 'preserveObjectReferences' boolean set to true. The outputmessage of line 16 is shown here:

<Person xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="myNamespace">
    <FieldedNumbers xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" z:Id="2" z:Size="2">
        <d2p1:int>12</d2p1:int>
        <d2p1:int>24</d2p1:int>
    </FieldedNumbers>
    <FirstName z:Id="3">Ruurd</FirstName>
    <LastName z:Id="4">Boeke</LastName>
    <Numbers xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" z:Id="5" z:Size="2">
        <d2p1:int>31</d2p1:int>
        <d2p1:int>29</d2p1:int>
    </Numbers>
    <Orders z:Id="6" z:Size="2">
        <Order z:Id="7">
            <Amount>12</Amount>
            <Customer z:Ref="1" i:nil="true" />
            <OrderID>12</OrderID>
            <ProductID>19</ProductID>
        </Order>
        <Order z:Id="8">
            <Amount>12</Amount>
            <Customer z:Ref="1" i:nil="true" />
            <OrderID>13</OrderID>
            <ProductID>255</ProductID>
        </Order>
    </Orders>
    <PersonID>23</PersonID>
    <Something>666</Something>
</Person>

Now, I have had long conversations with the PM of WCF, in the days it was still called Indigo, about the way this is serialized. I think it was harder to actually set the boolean back then, because the xml created here is not platform independent. Look at all the z:ref and z:ID attributes. I hate them!!

This serialization method is only understood by WCF, so you need both WCF on the client and on the server.
That's not really an issue for most, and there does not exist a standard for serializing object graphs. So there just is not a way to solve this issue. However, there is a way to be more friendly and get rid of the ugly z:ID and z:Ref attributes!!

Interested?? Read on.

My Solution: create surrogates on the fly

I use PostSharp to create a surrogate type during the compilation phase. Then I use a DataContractSurrogate to substitute these during serialization and deserialization.

All you need to do is attach that surrogate on your service (I have created an attribute for that) and use an attribute on your domainclasses to inform PostSharp to do it's magic:

    [CreateSerializeSurrogate]
    [DataContract(Namespace = "myNamespace", Name = "Person")]
    public class Person
    {
        [DataMember]
        public int PersonID { get; set; }
}

So, the new XML that is being sent on the wire, looks like this:

  1 <Person xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="myNamespace">
  2     <FieldedNumbers xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
  3         <d2p1:int>12</d2p1:int>
  4         <d2p1:int>24</d2p1:int>
  5     </FieldedNumbers>
  6     <FirstName>Ruurd</FirstName>
  7     <LastName>Boeke</LastName>
  8     <Numbers xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
  9         <d2p1:int>31</d2p1:int>
 10         <d2p1:int>29</d2p1:int>
 11     </Numbers>
 12     <Orders>
 13         <Order>
 14             <Amount>12</Amount>
 15             <Customer>
 16                 <SerializationID>0</SerializationID>
 17             </Customer>
 18             <OrderID>12</OrderID>
 19             <ProductID>19</ProductID>
 20             <SerializationID>1</SerializationID>
 21         </Order>
 22         <Order>
 23             <Amount>12</Amount>
 24             <Customer>
 25                 <SerializationID>0</SerializationID>
 26             </Customer>
 27             <OrderID>13</OrderID>
 28             <ProductID>255</ProductID>
 29             <SerializationID>2</SerializationID>
 30         </Order>
 31     </Orders>
 32     <PersonID>23</PersonID>
 33     <SerializationID>0</SerializationID>
 34     <Something>666</Something>

35 </Person>

That's much better!

Look at line 33, where our person is assigned an ID. Now, checkout lines 16 and 25: Our Order object is pointing to our already serialized Person class.

How does it work
The CreateSerializeSurrogate attribute

If you have read my posts about PostSharp, you know that it is a framework that is able to alter the assemblies you create after they have been compiled. The CreateSerializeSurrogate instructs PostSharp to create a nested type inside your domainobject, called xxxSurrogate. That surrogate class has a constructor taking in the original type. It copies all values to it's own values. However, it is also passed a List<object>. It checks whether the original object was already serialized. If it was, no values will be copied, except the serializationID. So, at line 15 for instance, you see a complete person instance serialized, but just without it's values.

PostSharp also introduces a Deserialize(List<object>) method, that will do the reverse.

Let's take a look at the result using reflector:

  1 [DataContract(Namespace="myNamespace", Name="Person")]
  2 public class Person
  3 {
  4     // Fields
  5     [CompilerGenerated]
  6     private string <FirstName>k__BackingField;
  7     [CompilerGenerated]
  8     private string <LastName>k__BackingField;
  9     [CompilerGenerated]
 10     private List<int> <Numbers>k__BackingField;
 11     [CompilerGenerated]
 12     private List<Order> <Orders>k__BackingField;
 13     [CompilerGenerated]
 14     private int <PersonID>k__BackingField;
 15     [DataMember]
 16     public List<int> FieldedNumbers;
 17     [DataMember]
 18     public int Something;
 19 
 20     // Methods
 21     static Person();
 22     public Person();
 23     public void CopyDataFromSurrogate(PersonSurrogate surrogate);
 24 
 25     // Properties
 26     [DataMember]
 27     public string FirstName { [CompilerGenerated] get; [CompilerGenerated] set; }
 28     [DataMember]
 29     public string LastName { [CompilerGenerated] get; [CompilerGenerated] set; }
 30     [DataMember]
 31     public List<int> Numbers { [CompilerGenerated] get; [CompilerGenerated] set; }
 32     [DataMember]
 33     public List<Order> Orders { [CompilerGenerated] get; [CompilerGenerated] set; }
 34     [DataMember]
 35     public int PersonID { [CompilerGenerated] get; [CompilerGenerated] set; }
 36 
 37     // Nested Types
 38     [DataContract(Name="Person", Namespace="myNamespace")]
 39     public class PersonSurrogate
 40     {
 41         // Fields
 42         [DataMember(EmitDefaultValue=false)]
 43         public List<int> FieldedNumbers;
 44         [DataMember(EmitDefaultValue=false)]
 45         public string FirstName;
 46         [DataMember(EmitDefaultValue=false)]
 47         public string LastName;
 48         [DataMember(EmitDefaultValue=false)]
 49         public List<int> Numbers;
 50         [DataMember(EmitDefaultValue=false)]
 51         public List<Order> Orders;
 52         [DataMember(EmitDefaultValue=false)]
 53         public int PersonID;
 54         [DataMember]
 55         public int SerializationID;
 56         [DataMember(EmitDefaultValue=false)]
 57         public int Something;
 58 
 59         // Methods
 60         public PersonSurrogate();
 61         public PersonSurrogate(Person source, List<object> graphList);
 62     }
 63 }
 64 
 65  
66

I have chosen not to expand methods. See how there is a complete PersonSurrogate class, that you will not find in your sourcecode. Also, a CopyDataFromSurrogate exists! The constructor on line 61 is shown here:

public PersonSurrogate(Person source, List<object> graphList)
{
    if (!graphList.Contains(source))
    {
        graphList.Add(source);
        this.PersonID = source.PersonID;
        this.FirstName = source.FirstName;
        this.LastName = source.LastName;
        this.Numbers = source.Numbers;
        this.Orders = source.Orders;
        this.Something = source.Something;
        this.FieldedNumbers = source.FieldedNumbers;
    }
    int ListID = graphList.IndexOf(source);
    this.SerializationID = ListID;
}

Well, there you go.

Doing this in PostSharp was not the easiest thing, but also not the hardest. I did it, so you don't have to. Let's look at a small piece of the weaver:

        private void CopyFieldsToSurrogate(InstructionWriter writer)
        {
            foreach (FieldDefDeclaration field in source.Fields)
            {
                if (field.CustomAttributes.FirstOrDefault(attr => attr.ConstructRuntimeObject() is DataMemberAttribute) != null)
                {
                    // has datamember
                    FieldInfo finfo = field.GetReflectionWrapper(null, null);
                    FieldDefDeclaration f = new FieldDefDeclaration();
                    surrogate.Fields.Add(f);
                    f.Name = field.Name;
                    f.FieldType = field.FieldType;
                    f.Attributes = System.Reflection.FieldAttributes.Public;
                    // copies the datamember attribute in the sourcecollection
                    CopyDataMemberAttribute(field.CustomAttributes, f.CustomAttributes);
                    // init the field
                    writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                    writer.EmitInstruction(OpCodeNumber.Ldarg_1);
                    writer.EmitInstructionField(OpCodeNumber.Ldfld, field);
                    writer.EmitInstructionField(OpCodeNumber.Stfld, f);
                }
            }
            writer.EmitInstruction(OpCodeNumber.Nop);
        }

Ouch. I did not use the highlevel Laos, but used the core functionality to really insert IL. This means there is no runtime overhead at all.

The datasetsurrogate

The tricky part was to get a list to the objects during serialization. I have created a datacontractsurrogate for that, and an attribute to place it on your service:  [AttachSurrogateAttribute]

I'm not particularly pleased with the internals of the surrogate. I ran into some big problems getting a context for the operation: it is discarded right before the datacontractsurrogate goes to work, and the client-side does not have a operationcontext.current at all. So I had trouble getting a solution that will maintain a list only for the time I need it.

I had to power-through it. However there is absolutely some extension point I have overlooked that would work for this.

For now, I have two surrogates: one for the client (which does not use operationcontext.current) and one for the service. It works great though. I'll figure out a better way, or comment me if you know one.

Conclusion

If you are happy with the representation of the xml that is sent by the preserveObjectReferences boolean, then that's fine. If you are not, the downloadable solution below will fix this quite handily. If you need to interop with some other platform, I think they can come to grips with your contract.
I just want to send over nice xml. ;-)

Download

You can download the solution here. No need to install PostSharp or anything.
Remember, the Server project is going to self-host, so if you want to start it, start VS in admin mode.
Now, if you want to see it work, you can either start both Server and Client projects and press the nice buttons. Set breakpoints to see that your graph went over just fine.
Use the unittests to see how the graph looks.

[disclaimer: I made this project in a rush. As I said the datacontractsurrogate was not implemented nicely. Also, I know I could have done better by implementing an interface on the created surrogate class, so I could call it's methods better..  If there is interest in these things, I might spend more time on it. For now, I'll leave it at this]

[disclaimer 2: forgot to mention: this will probably not work on generic domain objects. If there is interest, I will spend the time to make that work]

 

kick it on DotNetKicks.com

Monday, 03 March 2008 14:32:14 (Romance Standard Time, UTC+01:00)  #    Comments [6]  |  Trackback