Tuesday, March 11, 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, March 11, 2008 6:02:57 PM (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, March 11, 2008 2:19:12 PM (Romance Standard Time, UTC+01:00)  #    Comments [12]  |  Trackback
 Monday, March 10, 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, March 10, 2008 5:56:40 PM (Romance Standard Time, UTC+01:00)  #    Comments [5]  |  Trackback
 Wednesday, March 05, 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, March 05, 2008 9:33:26 PM (Romance Standard Time, UTC+01:00)  #    Comments [0]  |  Trackback