繁体   English   中英

如何在EntityFramework 4.1中共享没有CodeFirst的dbContext?

[英]How do I share dbContext without CodeFirst in EntityFramework 4.1?

长时间潜伏,第一次海报。

我在这里找到了很多关于如何使用CodeFirst在存储库之间共享dbContext的东西,但我似乎无法将它与我正在处理的项目联系起来,它不使用代码或依赖注入。

首先,关于项目的一点背景,以确保我正在以正确的方式接近这一点。 我进入这个项目,他们首先使用EF4和DB。 我不是EF的专家,但我现在已经摸索了几个不同的项目。

我不得不实施几个不同的要求,迫使我介入他们的“服务”级别和数据库。 换句话说,他们的对象直接调用EF db对象,如

using (var db = new MYDB()){
  var bar = db.Foo
   .Include("Transactions")
   .Include("blah")
   .Where(...);

  //do stuff

  db.SaveChanges();
}

我必须做的一件事就是跟踪所有变化的字段,所以我抽出了一个级别,现在我们有了

FooObject bar = GetFooObject(...);
bar.Title = "asdfasdf";
//do stuff to bar
bar.Save();

它将所有字段包装到属性中,以便我可以注销任何更改。 在bar.save中,我打开一个db上下文,获取现有的Foo或创建一个新的,分配所有值,然后调用db.SaveChanges。

事实证明,他们也会根据交易和blah做很多子查询。 所以,当他们做类似的事情

var bar = GetFooObject(...);

var t = new Transaction();
//do stuff to t
...
bar.Transactions.Add(t);
bar.Save();

我遇到各种上下文错误,说dbcontext不再可用等等。我完全理解。 我不知道的是如何解决它。 我已经看到很多关于在使用之前创建dbContext然后将其传入的内容,但我似乎无法找到正确的方法来实现它,因此它将适用于我的代码。

我最近的尝试是基于几个关于如何将DBContext转换为ObjectContext的例子(这反过来基于我发现的所有关于共享连接引用ObjectContext而不是DBContext的连接的例子)如下所示:

using (var db = ((IObjectContextAdapter)(new FooDB())).ObjectContext)
  {
using (var context = new DbContext(db, false))
{
var bar = FooObject.GetFooObject(fooId);
Result r = bar.ProcTrans(amount,
                        transDate,
                            db.TransactionTypes
                              .Include(tt => tt.Description)
                              .SingleOrDefault(tt => tt.TypeID == transactionTypeId),
                            employeeId,
                            comment);

但是使用这段代码我得到一个错误,我没有为TransactionTypes定义。 它无法识别我的任何数据库对象。

如何创建一个DBContext并将其传递给我的FooObject,以便我可以保持它对相关的更新? 我甚至不知道我是否正确地提出这个问题。 如何在不重新编码整个事物的情况下弥合这一差距?

编辑

以下是自打开此问题以来我发现的一些事情。 也许其中一个会做到这一点。

嗯,这个发现肯定更像是重新编码整个事情,但我在寻找关于“做变更跟踪”与触发器响应的链接时发现了这一点。

poco在实体框架第3部分:用poco改变跟踪

我刚刚发现这个如何在asp.net中的各种模型库中共享数据上下文,这可能是一种简单的方法来处理它。

我会留下对象上下文的东西。

我在MVC应用程序中实现共享DBContext的方式如下:

public class BaseRepository
{
    public static MyAppContext GetDataContext()
    {
        string ocKey = "ocm_" + HttpContext.Current.GetHashCode().ToString("x");

        if (!HttpContext.Current.Items.Contains(ocKey))
            HttpContext.Current.Items.Add(ocKey, new MyAppContext());

        return HttpContext.Current.Items[ocKey] as MyAppContext;
    }
}

然后每当我需要进行数据库操作时,我都可以调用:

BaseRepository.GetDataContext().YourObjects.Where(x => ...);
....
BaseRepository.GetDataContext().SaveChanges();

只要您仍处于相同的HTTP上下文中,您将共享相同的数据库上下文。 不完全确定这会消除您所获得的错误,但它至少是一种分享您的上下文的方式。

对我来说,答案与我发布的一个链接有关。

如何在asp.net中的各种模型存储库之间共享数据上下文

当我看到这些类型的注射答案时,让我失望的是语法上它们对我不起作用。 我没有DataContext,也没有任何Repository模型,但我决定在概念上尝试并在各处传递Context。

基本上,我传递了与Object构造函数或创建新对象的任何工厂方法的连接,并将其存储在局部变量中,就像这样排序。

public class Foo{
    private MyDB _db;
    private Foo _foo;

    public FooObject(MyDB dbContext)
    {
        _db = dbContext;
    }

    public static FooObject GetFooObject(int FooID, MyDB db){
        bool closeFlag = false;

        //if null was passed in, then we will create our own connection and manage it
        if (db == null)
        {
            _db = new MyDB();
            closeFlag = true;
        } else {
            //otherwise, we set our local variable
            _db = db;
        }

        //from now on, all queries are done using the local variable
        var _f = _db.Foos
            .Include("x")
            .Include("y")
            .Include("z")
            .SingleOrDefault(f => f.FooID == FooID);

        var fo = FooObjectFromFoo(_f, db);

        if (closeFlag)
            db.Dispose();

        return fo;
    }

    // This copies all of the values from Foo and puts the into a FooObject
    public static FooObject FooObjectFromFoo(Foo f, MyDB dbContext){
        if (l == null)
            return null;

        // note that we pass the dbContext to the constuctor
        FooObject _f = new FooObject(dbContext){
            _foo = f,
            ...
            //note x, y, and z are the other EF "table references".  I'm not sure what you technically call them.
            x = f.x,
            y = f.y,
            z = f.z
        };

        return _f;
    }


    //we call this to save the changes when we're done
    public bool Save(){
        bool close = false; 
        bool retval = true;

        MyDB db = _db;

        //remember we set _db in the constructor            
        if (db == null) {
            db = new MyDB();
            close = true;
        }

        try
        {
            // a reference to this row should have been saved in _foo if we loaded it from the db.
            // take a look at FooObjectFromFoo
            if (_foo == null)
            {
                _foo = db.Foos.SingleOrDefault(x => x.FooID == _FooID);
            }
            if (_foo == null)
            {
                _foo = new Foo();
            }
            //copy all my object values back to the EF Object
            _foo.blah = blah;
            _foo.x = x;
            _foo.y = y;
            try
            {
                //save the new one.
                db.SaveChanges();

            }
            catch (DbEntityValidationException dbEx)
            {
                TransactionResult.AddErrors(dbEx);
                retval = false;
            }
        }
        catch { throw new Exception("Something went wrong here.");}
        finally { if (close) db.Dispose(); } //if we created this connection then let's close it up.
    }
}

现在在我的方法中,我总是使用本地_db连接。 在我的FooObject之外,我们有一个FooService,它是从所有控制器调用的。 因此,当实例化FooService时,我使用下面的类创建数据库连接。 如果我理解正确,这应该给我一个在我的服务请求期间存在的上下文,在我的情况下,相当可靠地模仿请求。

namespace My.Domain
{
  public class MyDataContext : IDisposable  {
    private MyDB _context;
    private bool _ownContext;

    public MyDataContext(){
        _context = new MyDB();
        _ownContext = true;
    }

    public MyDataContext(MyDB db)
    {
        _context = db;
        _ownContext = false;
    }

    public MyDB Context
    {
        get { if (_context == null) { _context = new MyDB(); _ownContext = true; } return _context; }
        set { _context = value; }
    }

    public bool OwnContext
    {
        get { return _ownContext; }
        set { _ownContext = value; }
    }

    public void Dispose()
    {
        if (_context != null && _ownContext)
            _context.Dispose();
    }
  }
}

在FooService中我做这样的事情。

    private MyDb db;

    public FooService (){
        var _db = new MyDataContext();
        db = _db.Context;
    }

    public Result ProcessTransaction(int FooId, string comment)
    {
        var foo = FooObject.GetFooObject(FooId,db);

        Result r = foo.ProcessTransaction(comment);

        if (r.Success)
            foo.Save();

        return r;
    }

我认为这样做“正确”我应该只在关闭上下文时保存更改...但我已经在我的FooObject上有一个Save方法,所以我只是在那里调用db.SaveChanges。

我知道有很多方法可以改善这一点,我相信随着时间的推移我会实施其中的一些,但就目前而言,这就是诀窍。 这就是我解决所有“上下文不再可用”的问题,而这个对象来自不同的上下文错误。

在查看其他人的例子时,让我感到震惊的是他们都在使用CodeFirst和某种依赖注入。 他们通常使用Repository模式,但我们没有。 但事实证明,我只是必须实施我自己的黑客连接注入的本地化版本! :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM