Wednesday, February 03, 2010

Mocking Entity Framework ObjectSets

After finally getting my head around object-graph deletes in Entity Framework v4, I set about getting some better testing around my EF code.  The first step was updating my self-tracking entities template to have my object-sets be exposed as IObjectSets which involved a small tweak to my TT file.

I ran into some challenges using a Rhino Mock of IObjectSet, so I let Google do some work for me and I landed on Jamie Phillip’s post about mocking IObjectSet via his MockObjectSet., which was exactly what I was looking for.  Adding this class into my test library got me 90% of where I wanted to be, but I still had compile errors on the spots where I was building out my object-graphs with “.Include(“”)” calls. 

After some time digging through the EF4 code with Reflector,  I was not finding what I thought I needed, an IObjectQuery interface, which would allow me to mock the Include method.  Again, I went back to Google and ran across a MSDN Forum post which Diego Vega answered with an almost too obvious solution to my exact problem: use an extension-method for Include.

Since I was only interested in mocking out the “Include” behavior, and have my MockOjectSet return my configured entity list, I went with Diego’s first suggestion for Include, which looks like this:

public static IQueryable<T> Include<T>(this IQueryable<T> source, string path){
   var objectQuery = source as ObjectQuery<T>;
   if (objectQuery != null){
      return objectQuery.Include(path);
   }
   return source;
}

The only pain point to this approach to testing, is that I’m stuck remembering that I need to add a new extension method whenever I utilize one of the methods in the ObjectQuery<T> class – Union, Distinct, etc.

Sunday, January 31, 2010

Using log4net with .Net 4.0 WPF Applications

I ran into some issues trying to get log4net up and running on my current project, which is being written in VS2010 Beta2, targeting the .net 4.0 framework.  I went about adding a reference to log4net, setup my app.config with a rolling log-file appender and added a call to XmlConfigurator.Configure() in one of my classes.

I then went to compile and got the following error: The type or namespace name 'log4net' could not be found (are you missing a using directive or an assembly reference?) 

I double-checked, and yes, I had indeed added a reference to log4net, so what was wrong?  Turns out, for .net 4.0 WPF projects, the default Target Framework is the “.Net Framework 4 Client Profile”, which is a subset of the full framework with the goal of shrinking footprint of the .net framework needed to download for a typical windows application.  However, log4net apparently needs access to system.web.dll, which isn’t included in the client profile. 

To fix your app so it’ll compile/run, open your project’s properties page, and set the Target Framework to “.Net Framework 4” and save your changes. 

Saturday, January 30, 2010

Entity Framework v4 Object-Graph Deleting Quirks

Update: Danny was quick to respond with a great explanation about how EF4 deals with object-graphs and deletes.

I’ve been getting up to speed on Entity Framework V4 (EF4) lately, for use in a WPF application that I’m working on.   One of the issues I’m coming up against is removal of objects in one-to-many and many-to-many relationships.  It’s still not clear to me why things work the way they do in EF4, but I’ll lay out a few scenarios and talk about what works and what doesn’t work and hope that someone can later comment as to why things may not be working as I would expect.

Scenario 1: A many-to-many relationship where the join table has its own primary key (an identity column in this case):

image

Given the following code, I’d expect to be able to remove an object from the Employee.ProjectAssignments collection and see that object removed from the database when I call SaveChanges on my ObjectContext:

using (var context = new EFTestDBEntities()){
                var employeeGraph = (from e in context.Employees.Include("ProjectAssignments").Include("ProjectAssignments.Project") where e.ID == employeeID select e).FirstOrDefault();
                if (employeeGraph != null){
                    var assignment = employeeGraph.ProjectAssignments.First();
                    employeeGraph.ProjectAssignments.Remove(assignment);

                    context.SaveChanges();
                }
            }

When I hit the “SaveChanges()” call, I get the following exception:

System.InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

This strikes me as strange – I assume that I’ve indicated that the employee is no longer related to this project-assignment, so the object is essentially “worthless” and I’d expect it to be removed.

In order to successfully remove this project-assignment, I had to do the following:

using (var context = new EFTestDBEntities()){
                var employeeGraph = (from e in context.Employees.Include("ProjectAssignments").Include("ProjectAssignments.Project") where e.ID == employeeID select e).FirstOrDefault();
                if (employeeGraph != null){
                    ProjectAssignment assignment = employeeGraph.ProjectAssignments.First();
                    context.ProjectAssignments.DeleteObject(assignment);

                    context.SaveChanges();
                    Assert.AreEqual(0, employeeGraph.ProjectAssignments.Count);
                }
            }

Okay, so if I “manually” delete the object by the explicit call to DeleteObject, things work.  So far so good, I can work with this.

Scenario 2: A many-to-many relationship where the join table has a primary key that is composed by foreign-keys to the tables on either side of the relationship:

image 

In this case, the code I’d expect to work, actually worked and allowed me to delete a class-schedule from a teacher:

using (var context = new EFTestDBEntities()){
                var teacherGraph = (from t in context.Teachers.Include("ClassSchedules").Include("ClassSchedules.Class") where t.ID == teacherID select t).FirstOrDefault();
                if(teacherGraph != null){
                    var classSchedule = teacherGraph.ClassSchedules.First();
                    teacherGraph.ClassSchedules.Remove(classSchedule);

                    context.SaveChanges();
                }
            }

Why does EF4 make a distinction in behavior between these two scenarios that are essentially the same operation yet one object-graph allows me to write what I naturally want to write (scenario 2), but not the other?

Scenario 3: A one-to-many relationship:

image

Once again, I tried to write the code in a way I’d expect to naturally work:

using (var context = new EFTestDBEntities()){
                var orderGraph = (from o in context.Orders.Include("OrderDetails") where o.ID == orderID select o).FirstOrDefault();
                if(orderGraph != null){
                    var detail = orderGraph.OrderDetails.First();
                    orderGraph.OrderDetails.Remove(detail);

                    context.SaveChanges();
                }
            }

And once again, I got the following exception:

System.InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

As with scenario 1, I had to write the following code to successfully remove the OrderDetail record:

using (var context = new EFTestDBEntities()){
                var orderGraph = (from o in context.Orders.Include("OrderDetails") where o.ID == orderID select o).FirstOrDefault();
                if (orderGraph != null){
                    var detail = orderGraph.OrderDetails.First();
                    context.OrderDetails.DeleteObject(detail);

                    context.SaveChanges();
                }
            }

Alright, so I have managed to work around some of the quirks with EF4, but I really don’t understand why removal of objects in the object-graph don’t cause a proper remove/delete in some cases, yet work just as I expect in others. 

Saturday, August 29, 2009

LINQ to SQL SubmitChanges Order of Operations

I did not realize the following is true in LINQ to SQL: Calling SubmitChanges fires ChangeSet changes in the following order:

  • Inserts
  • Updates
  • Deletes

Normally, this might not be a big deal, and you might never notice it, but it was the source of some pain on my current project.

Consider an order-entry screen that allows the user to add/edit/delete line items on an order.  The order-detail records are keyed by order-number/line-number, and this is the source of the problem.  If the user deletes line #1 in the order, what was originally line #2 gets re-ordered as line #1.  When the user decides they are done editing the order, they will then hit the save button and the queued up add/edit/delete operations are committed via the LINQ to SQL SubmitChanges command.

Fortunately, we were able to change the primary-key on our order-detail table, but we effectively lost some automatic data-integrity since we were previously able to ensure uniqueness on order-number/line-number.  For teams that don’t have the luxury of changing their database keys, this design decision by the LINQ to SQL team may be cause to move away from LINQ to SQL. 

Why the LINQ to SQL team decided to not submit changes in the order of operations, or allow the user to specify a non-default order to submit changes is beyond me.  I’m sure they had valid reasons for this decision, but it’s a bit short-sighted in that this interaction pattern is fairly common in order-entry system. 

This could perhaps been avoided by stamping various LINQ class members as virtual and letting us developers (consumers) override behaviors as we need – and assume the risks that come with overriding the framework code.

Tuesday, July 14, 2009

Linq to SQL Object Equality Issue

I started having some issues where Linq to SQL objects are "different” instances between calls to db.GetTable(type) and comparing objects returned.  In the majority of my types, object equality was working just fine, but in a few types, the hash-codes for otherwise “equal” objects was different, while the remainder of the object data was as expected.

Deleted the designer-objects and re-added to my DBML and the issue is resolved. 

A compare of my DBML file shows that the “IsPrimaryKey” attribute on my Identity columns was missing.  I had probably added the table to the DBML and realized later that I hadn’t specified a primary key and later added said key, without re-synchronizing my DBML.