2

我正在使用实体框架代码优先方法和 mvc4 Web 应用程序构建一个分层应用程序,主要以DataServicesWeb.

在我的网站上,我这样做:

public void Foo() {
    EntityService _svc = new EntityService();
    Entity = _svc.FindById(1);
}

服务方法如下所示:

private readonly MyContext _ctx = new MyContext();

public Entity FindById(long id) {
    return _ctx.Entities.SingleOrDefault(q => q.EntityId == id);
}

问题是当我需要使用多个服务时,因为每个服务都会创建自己的上下文。

试图解决这个问题我做了这样的事情:

public class MyContext : DbContext {
    private static MyContext _ctx;

    public MyContext() : base("name=myConnectionString") { }

    public static MyContext GetSharedInstance() {
        return GetSharedInstance(false);
    }

    public static MyContext GetSharedInstance(bool renew) {
        if(_ctx == null || renew)
            _ctx = new MyContext();

        return _ctx;
    }
}

将我的服务更改如下:

public class EntityService
{
    private readonly MyContext _ctx;

    public bool SharedContext { get; private set; }

    public EntityService()
        : this(false) { }

    public EntityService(bool sharedContext)
        : this(sharedContext, false) { }

    public EntityService(bool sharedContext, bool renew)
    {
        SharedContext = sharedContext;

        if (SharedContext)
            _ctx = MyContext.GetInstance(renew);
        else
            _ctx = new MyContext();
    }
}

现在,如果我想分享我的上下文的一个实例,我会做这样的事情:

EntityService _entitySvc = new EntityService(true, true);
AnotherEntityService _anotherEntitySvc = new AnotherEntityService(true);

这至少是克服这个问题的好方法吗?我将不胜感激提供的任何帮助。谢谢。

4

2 回答 2

11

永远永远永远永远永远永远...永远...我有没有提到过?我是认真的。永远不要创建静态数据上下文。永远不能。真的。我可以再强调一点吗?绝不。甚至不要考虑它。想想它会让你患上脑癌。

这是依赖注入真正大放异彩的情况之一。使用依赖注入,您可以管理数据上下文的生命周期,并使它们在请求的生命周期内有效,而不是像静态一样在应用程序池的生命周期内有效。

详细说明为什么共享上下文不好。不仅在您的类之间共享上下文,而且在线程和请求之间共享。这意味着同时使用该站点的两个用户将相互踩踏彼此的数据上下文,从而导致各种问题。数据上下文不是线程安全的,也不是并发安全的。

如果要与多个对象共享数据上下文,则需要将其作为方法调用的一部分或作为构造函数参数传递给这些对象。

但是,我强烈建议通过依赖注入来执行此操作。

于 2012-09-14T17:48:39.507 回答
2

我偶然发现了这个寻找别的东西,我最近为实体框架构建了一个存储库模式。它旨在封装框架,同时让自己的存储库模式大放异彩。你会想要听从神秘人的建议。这是我建议您如何处理它的粗略示例。

public class MyContext: DbContext, IUnitOfWork
{
    public IDbSet<Note> Notes { get; set; }
    public IDbSet<DomainType> DomainTypes { get; set; }
    public IDbSet<DomainValue> DomainValues { get; set; }
    public IDbSet<Party> Parties { get; set; }

    public MyContext() :base()
    {
        Configuration.LazyLoadingEnabled = false;
        Configuration.ProxyCreationEnabled = false;
    }
}

如您所见,DbContext 是一个 IUnitOfWork,其上仅包含 SaveChanges 方法。这就是您要传递到存储库中的内容。

public abstract class BaseEfRepository<T, TU> : IRepository<T>
    where T : class 
    where TU : IUnitOfWork
{
    ...
    virtual public T GetSingle(Expression<Func<T, bool>> whereCondition)
    {
        Log.DebugFormat("Called GetSingle<{0}>()...", typeof(T));
        return MyEntitySet.Where(whereCondition).FirstOrDefault();
    } [etc...]

}

这将包含您的大部分基本存储库样式的集合访问器以及您想要封装的任何内容。UnitOfWork 被传递到存储库中,因此您可以在同一事务中拥有任意数量的存储库。

public class NoteRepository: BaseEfRepository<Note, MyContext>
{
    public NoteRepository(MyContext uow) : base(uow)
    {
        MyEntitySet = uow.Notes;
    }
    public Note GetSingleWithParties(Expression<Func<Note, bool>> whereCondition)
    {
        return [... whatever ...]
    }

}

我希望这被证明是有用的。

于 2013-03-28T19:00:30.143 回答