views:

74

answers:

2

I've been running into a problem where using objects from two datacontexts that are of same type, won't submit objects on. To make this matter simple consider the following LINQ-to-SQL design where we have database tables describing cars and persons.

+--------------+         +--------+
| Car          | 1     1 | Person |
+--------------+---------+--------+
| Registration |         | name   |
| ownerId {FK} |         +--------+
+--------------+

Then we've got two data repositories that have an instance to the same datacontext class having the following methods:

public OwnerRepository {
    private MyDataContext db;

    public Person GetOwnerByName(string ownerName)
    {
        return (from person in db.Persons
               where person.Name == ownerName
               select person).SingleOrDefault();
    }
}

public CarRepository {
    private MyDataContext db;

    public Car GetCarByRegistration(string registration)
    {
        return (from car in db.Cars 
               where car.Registration == registration
               select car).SingleOrDefault();
    }

    public void RegisterOwner(Person owner, string registration)
    {
        var car = GetCarByRegistration(registration);
        car.Owner = owner;
        db.SubmitChanges();
    }
}

Say we get a Person from the OwnerRepository and use it to register that person as a owner to a car:

var owner = ownerRepository.GetOwnerByName("Peter Pan");
carRepository.RegisterOwner(owner, "TOO COOL");

The method RegisterOwner will throw an exception because the Person object won't be recognized by the datacontext (even though it is of the same type). What can you do to go around this problem?

+2  A: 

Why do you have two different DataContexts to start with? That sounds like a bad idea. Why not give the CarRepository a constructor where you pass in the DataContext? Then both repositories can use the same DataContext.

Using a single DataContext means the submits can go as one transaction, and you won't get strange problems where the caches in the contexts have different ideas of what's in the database.

Jon Skeet
Yeah, it's just an example. I do have constructors that can take datacontext as parameters in my "ACTUAL" repository classes. Looks like I need a builder class that can construct all the repositories with the same datacontext.
Spoike
Right - I'm pretty sure that's the right solution. Mixing and matching objects from different contexts sounds like a really bad idea to me.
Jon Skeet
most people using this decomposition tend to gravitate towards using an IOC Container, which basically takes care of the "builder" part for you (some examples are NInject, Castle Windsor, Spring .NET, Unity...)
Denis Troller
Jon Skeet: It actually doesn't work to mix and match objects in different contexts just out of the box. I was hoping someone knew how to attach an object from one context into another. :)
Spoike
Denis Troller: Never used IOC Containers before but it seems like I've ran into an actual use for one right now. Thanks for the tip.
Spoike
+1  A: 

Linq2sql doesn't support using cross datacontext objects directly -serialize/deserialize/attach is really not worth it.

The scenario that you have is a result of coupling between the repositories. You are creating an internal dependency on it using the same datacontext class, which is revealed on the RegisterOwner code trying using the received entity directly.

Consider what happens if the calling code updates something on the person entity, should the RegisterOwner's code really be saving those changes?

This type of coupling at the repository level might be ok inside the same bounded context. If these are supposed to be pieces of independent sub-systems the car repository would focus on saving only the information it is meant to handle, which is a subset of owner info that identifies the user in the car system. This is what allows you to switch pieces along the way. Of course, this makes more sense in more complex scenario where you want to have higher degrees of separation.

Notice that on the sample posted you are not really needing to update anything on Person, so you could effectively go with a version that only sets the person id on the car owner info, instead of assigning it the full Person instance.

For the more coupled scenario, you can use Jon's answer - give them the same DataContext instance.

eglasius