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.