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