3

长期潜伏者,第一次海报。

我在这里找到了大量关于如何使用 CodeFirst 在存储库之间共享 dbContext 的内容,但我似乎无法将其与我正在处理的项目联系起来,该项目不使用代码优先或依赖注入。

首先,了解一下该项目的背景知识,以确保我以正确的方式处理这个问题。我参加了这个项目,他们首先使用的是 EF4 和数据库。我远非 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。

事实证明,他们还基于 Transactions 和 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 中的各种模型存储库之间共享数据上下文,这可能是一种简单的方法。

4

2 回答 2

1

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

我在 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 上下文中,您将共享相同的 DB 上下文。不完全确定这会消除您遇到的错误,但它至少是一种分享您的上下文的方式。

于 2012-12-12T18:55:02.990 回答
0

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

如何在 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 和某种依赖注入。他们通常使用存储库模式,而我们没有。但事实证明,我只需要实现我自己破解的本地化版本的连接注入!:)

于 2012-12-12T21:46:37.810 回答