views:

111

answers:

1

I'm thinking about starting a new project using EF 4 and going through some articles, I found an article about EF with repository pattern and unit of work (http://blogs.msdn.com/b/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx)

Looking at that article, it uses the ObjectContext as the UnitOfWork and it passes it to the Repository.

My question is what if I have 2 ObjectContext which mean I will have 2 unit of work, but I actually wants all the operation perform on those 2 context to be one single unit of work, is this scenario possible? I don't want to call save on each context, I'd like it to be transactional .... without using transactionscope ...

For example, I have a context that manages Operation Log and another context that manages Orders. Lets say In my business layer, I have a method called AddOrder(). AddOrder() will use the order context to create a new order, but it will also use the operation log context to create a new operation log entry. Since those are 2 context, I'll have to call save on both context to commit .... maybe the only option is to have only one single context ....

EDIT: I meant 2 context of different types for example: OperationalLogContext and OrderContext.

+1  A: 

Yep - i believe it's possible.

The kicker is how handle your Repositories.

For example, each Repository should take a Context .. so just create one context and pass it to each repository.

(code please!) Glad u asked :)

public interface IOrderRepository
{
    IQueryable<Order> FindAll();
}

public interface IOperationLogRepository
{
    IQueryable<OperationLog> FindAll();
}

public interface IUnitOfWork
{
    void Commit();
}

.

public class SqlServerContext : ObjectContext, IUnitOfWork
{
    public void SqlServerContext(string connectionString) 
        : base(connectionString)
    {
    }

    public void Commit()
    {
        this.SaveChanges();
    }

    // Your other POCO's and stuff here ..etc..
}

.

public class OrderRepostiory : IOrderRepository
{
    private readonly SqlServerContext _sqlServerContext;
    public void OrderRepostiory(SqlServerContext sqlServerContext)
    {
        _sqlServerContext = sqlServerContext;
    }

    public IQueryable<Order> FindAll()
    {
        _sqlServerContext.Orders;
    }
}

.. and finally, instantiation. Cause your a good boy/girl/rainbow unicorn, you would be using Dependency Injection ..

public class SqlServerRegistry : Registry
{
    public SqlServerRegistry(string connectionString)
    {
        For<SqlServerContext>()
            .HybridHttpOrThreadLocalScoped()
            .Use<SqlServerContext>()
            .Ctor<string>("connectionString")
                .Is(connectionString);

        For<IOrderRepository>().Use<OrderRepository>();
        For<IOperationLogRepository>().Use<OperationLogRepository>();
    }
}

and because the SqlServerContext is defined as HttpOrThreadLocal, it will be instantied ONCE and reused in multiple Repositories.

Don't know or understand DI/IoC ?

then this would also work....

private SqlServerContext _sqlServerContext;
private IOrderRepository _orderRepository;
private IOperationLogRepository _operationLogRepository;

[TestInitialize]
public void TestInitialise()
{
    _sqlServerContext = new SqlServerContext(
             ConfigurationManager.AppSettings["connectionString"]);
    _orderRepository = new OrderRepository(_sqlServerContext);
    _operationLogRepository= new OperationLogRepository(_sqlServerContext);
}

[TestMethod]
public void SomeTest()
{
    // Arrange.
    const int count = 10;

    // Act.
    var orders = _orderRepository.FindAll().Take(10).ToArray();

    // Assert.
    Assert.IsNotNull(orders);
    CollectionAssert.AllItemsAreNotNull(orders);
    Assert.AreEqual(count, orders.Length);
}

once more, that's all untested code which i just typed up, as I was thinking about answering this question.

HTH.

Pure.Krome
Thanks, this helps alot. So the only way is to really have only one context... and if I have 2 contexts, then I'll have no choice but to resort to some transaction mechanism, is this correct?
pdiddy
nope. u can have as many as you want. notice how, with the Dependency Injection, I said HttpScopeOrThreadScope ? You can change that to be singleton (bad idea) or the default which is to `new` up a new instance every time an object is requested. So it's up to you :) If you doing the 2nd way (which i did using a Test example), then yeah .. u'll have to manually new up each context, one by one. eg `var context1 = new SSC(..); var context 2 = new SSC(...); _or = new OR(context1); _olr = new OLR(context2);` etc..
Pure.Krome
I had to update my answer - I had a serious bug in it. I replaced any reference to a `new EntityConnection(connectionString)` with a plain `connectionString` because the connection was not getting disposed of, if the context didn't create the `EntityConection` (instead of getting one passed into it). Cheers to Ayende Rahien (from Hibernating Rhino's) for the fix.
Pure.Krome
I meant 2 context of different types for example: OperationalLogContext and OrderContext. I think you were referring to multiple context of the same type (SqlServerContext) and that the operationalLog model and ordermodel reside inside the sqlservercontext. I meant the operatinallogcontext will manage the operational and the ordercontext manages the order model. Sorry for the confusion.
pdiddy
Ahh yeah .. - i misunderstood your question then :( I thought u wanted to *avoid* using 2 contexts because u have 2 repositories. Out of interest, why do you want 2x Contexts? Secondly - here's a related question, just asked by someone else: http://stackoverflow.com/questions/3717997/entity-framework-with-multiple-edmx
Pure.Krome
But thanks, your answer did help me understand more about the repository pattern and the unitofwork. It was more about having it structured, but it might now be a good idea after all.
pdiddy