C# Entity Framework:何时应该使用新的dbContext?

3

我一直在想,创建一个新的dbcontext实例应该采用什么正确方式? 我遇到了问题,因为当我通过SQL Server对数据库进行更改时,我的上下文并没有更新数据。 让我解释一下我们的网站如何工作。

我们正在为客户制作预约网站,显然需要将所有数据库托管在我们的服务器上。 当前应用程序有2个连接:

第一个连接

此连接始终与相同的数据库(称之为master)连接。它将根据其中包含的url代码将用户重定向到正确的数据库,例如: www.example.com/foo 服务器将检查这里的代码,即foo。然后,它会在表中查找匹配的代码,然后获取应该重定向的正确数据库名称,这就是我的第二个连接的出现。

第二个连接

这将根据主设备返回的数据进行正确的数据库连接。 到目前为止,一切似乎都很顺利,除了实际上从未更新的DBContext,因为我没有正确实例化它,并且我没有太多经验。 这是我和同事编写的代码:

using System;
using System.Data.EntityClient;
using System.Data.SqlClient;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Routing;
using WebRV.Models.Entities;

namespace WebRV.RouteDb
{

    public class DbConnection
    {

        private static DbConnection instance;
        private Cliniciel_WebRV_Entities db;
        private String connectionString;
        private readonly Cliniciel_WebRV_MasterEntities masterDb = new Cliniciel_WebRV_MasterEntities();
        private Int32 idCie;
        private static readonly object myLock = new object();


        private DbConnection() {
            var context = new HttpContextWrapper(System.Web.HttpContext.Current);
            var routeData = RouteTable.Routes.GetRouteData(context);
            // Use RouteData directly:
            String code = routeData.Values["code"].ToString();
            //String  code = Thread.CurrentContext. .RequestContext.RouteData.Values["code"].ToString();
            var response = masterDb.SYS_tbCustDBLocation.Where(p => p.CustDBLocationCode == code).ToList();

            if (response.Count == 1)
            {
                try
                {
                    db = CreateConnection(response.FirstOrDefault());
                    idCie = (db.SYS_vwCie.Where(p => p.ClinicielWebName == code).FirstOrDefault()).IdCie;
                }
                catch (Exception e)
                {
                    throw e;
                }

            }
            else {
                throw new FormatException();
            }
        }

        private Cliniciel_WebRV_Entities CreateConnection(SYS_tbCustDBLocation data)
        {

            connectionString = *****

            db = new Cliniciel_WebRV_Entities();

            db.Database.Connection.ConnectionString = connectionString;

            db.Database.Connection.Open();

            return db;
        }

        private static void CreateInstance() {
            instance = new DbConnection();
        }

        public static DbConnection GetInstance() {
            lock (myLock)
            {
                if (instance == null)
                {
                    CreateInstance();
                }

            }

            return instance;
        }

        public String GetConnectionString()
        {
            return connectionString;
        }

        public Cliniciel_WebRV_Entities GetConnection()
        {
            return db;
        }

        public Int32 GetIdCie()
        {
            //hard code 1 but test
            //return idCie;
            return 1;
        }

    }
}

以下是我使用它的示例:
  //[CompanyCodeFilter]
    public class HomeController : AppointementController 
    {
        //public static Cliniciel_WebRV_Entities entityDB = DbConnection.GetConnection();

        public HomeController()
        {
            base.Refresh();
        }

 public JsonResult GetConsultationDescription(Int32 idService)
        {
            //base.Refresh();
            entityDB.Set<SYS_vwService>().AsNoTracking();
            var motifDescription = entityDB.SYS_vwService.Where(s => s.IDLang == cultureName && s.IdService == idService && s.IdCie == idCie).FirstOrDefault();
            var base64 = Convert.ToBase64String(motifDescription.ServiceImage);
            var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
            var imageDecode = imgSrc;
            if (base64 == "AA==")
            {
                imageDecode = "";
            }
            var result = new { motifDescription, imageDecode };


            return Json(result, JsonRequestBehavior.AllowGet);
        }
  }

在这里,base.refresh() 调用了这个函数:

using System;
using System.Linq;
using WebRV.Filters;
using WebRV.Localization;
using WebRV.Models.Entities;
using WebRV.RouteDb;

namespace WebRV.Controllers
{
    //[CompanyCodeFilter]
    public class AppointementController : BaseController
    {
        protected Cliniciel_WebRV_Entities entityDB;
        protected Int32 idCie;
        protected String cultureName;

        //public AppointementController() {
        //    Refresh();
        //}

        //Permet de bien vérifier quel DB utilisé, quel idCie et quel cultureName.
        protected void Refresh() {
            entityDB = DbConnection.GetInstance().GetConnection();
            idCie= DbConnection.GetInstance().GetIdCie();
            cultureName = CultureLocalization.GetCulture();
            //cultureName = "en-ca";
        }

    }
}

如果有人能够帮助我正确地实例化连接,那将不胜感激,谢谢。


您是否遇到了需要解决的特定错误? - Vinod
3个回答

2
我认为您需要更好地了解db上下文是如何工作的。
它是许多设计模式的组合,但基本上,它是一个工作单元,用于跟踪表示数据的对象的更改。因此,它不应该被用作始终处于打开状态、来回切换的连接,虽然在一定程度上可以这样使用。
从您的代码中看,您正在使用它在ASP.NET MVC应用程序中。由于应用程序的性质,您可以采取以下两种方法之一:
- 每次需要使用它时创建db上下文; - 将db上下文作为控制器的属性创建。
最终,它们差不多都是一样的。我个人建议您在需要使用它时创建上下文实例,确保正确处理其释放。然后,您可以拥有一个基础控制器类,该类公开一个CreateConnection()方法来为您的上下文创建一个实例,就像您已经有的Cliniciel_WebRV_Entities CreateConnection()方法一样,只是您不需要显式打开连接,并且如果添加一个部分类并实现上下文的方法,则可以将连接字符串作为构造函数参数传递。就像这样:
public partial class Cliniciel_WebRV_Entities
{
    public Cliniciel_WebRV_Entities(string nameOrConnectionString):base(nameOrConnectionString)
    {

    }
}

您可以像这样使用它:
private Cliniciel_WebRV_Entities CreateConnection()
    {
        //Code to figure out which db to connect to (based on the other connection. You should consider caching that too if it doesn't change very often)
        var nameOrConnectionString = FigureOutConnection();
        var db = new Cliniciel_WebRV_Entities(nameOrConnectionString);
        return db;
    }

请记住,上下文取决于元数据,因此请确保您的连接字符串反映了这一点。

在您的代码中,您可以像这样使用它:

public JsonResult GetConsultationDescription(Int32 idService)
        {
            using(var entityDB = CreateConnection())
            {
                var motifDescription = entityDB.SYS_vwService.Where(s => s.IDLang == cultureName && s.IdService == idService && s.IdCie == idCie).FirstOrDefault();
                var base64 = Convert.ToBase64String(motifDescription.ServiceImage);
                var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
                var imageDecode = imgSrc;
                if (base64 == "AA==")
                {
                    imageDecode = "";
                }
                var result = new { motifDescription, imageDecode };


                return Json(result, JsonRequestBehavior.AllowGet);
            }
        }
  }

记住一件事,这也是新手容易忽略的地方:不应该在模型中将实体(上下文中的对象)传递给视图。因为一旦上下文被释放,具有导航属性的对象就会出现问题。虽然有解决方法,但并不推荐。应该将对象映射到模型中。

顺便提醒一下,在修改实体后,别忘了调用SaveChanges()方法,否则数据库将不会更新更改。



0
当您将DbContext传递给服务或业务类时,您将其作为值而不是引用传递,因此您将丢失客户端代码中所做的所有更改。选项是将其作为引用传递,但这不是安全的,也不是一个好的实践。相反,如果您将上下文作为委托传递,则直接传递对象的引用,因此这是保持从业务/服务层进行更改的注入上下文的最佳方法。 这是想法:
选项1,将其作为引用传递,不建议:
 public void Call()
    {
        //Attach changes to context, Passing as reference
        Process(ref _context);
        //All changes attached to context
        _context.SaveChanges();
    }

    public void Process(ref Cliniciel_WebRV_MasterEntities context)
    {
        var c = context();
        //Get entites dbcontext
        //Update entites
    }

选项 2,将其作为委托传递,最佳方法:

public void Call()
    {
        //Attach changes to context, Passing as reference
        Process(() => _context);
        //All changes attached to context
        _context.SaveChanges();
    }

    public void Process(Func<Cliniciel_WebRV_MasterEntities> context)
    {
        var c = context();
        //Get entites dbcontext
        //Update entites
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接