在 Entity Framework 上下文中使用单例模式 - 底层提供者打开失败
Using singleton pattern with Entity Framework context - The underlying provider failed on open
我正在尝试使用 Entity Framework 向我的 DbContext 添加单例模式。我一直为此使用单例模式,之前从未遇到过此错误。 我知道单例是最好的做法 (显然不是), 但是如果你们有空的话,能不能请解释为什么单例是最佳实践?
问题
除此之外,我收到此错误:
The underlying provider failed on open
让我们看看我的代码
DAO.cs
public class DAO
{
private static HourRegistrationEntities hourRegInstance;
public static HourRegistrationEntities HourRegInstance { get { return hourRegInstance = hourRegInstance ?? new HourRegistrationEntities(); } }
}
Service.cs(示例方法)
/// <summary>
/// Return a list of all denied Hour Registration for the Login with the given stringId
/// </summary>
/// <param name="stringId"></param>
/// <returns>A list of HourRegistrationDTO</returns>
public List<HourRegistrationDTO> GetAllDeniedHoursForLogin(string stringId)
{
var id = Int32.Parse(stringId);
using (var db = DAO.HourRegInstance)
{
var dbHours = db.HourRegistration.Where(x => x.LoginProject.Login.ID == id && x.Denied == true).ToList();
var returnList = new List<HourRegistrationDTO>();
foreach (var item in dbHours)
{
returnList.Add(new HourRegistrationDTO()
{
Id = item.ID,
Hours = item.Hours,
Date = item.Date.ToShortDateString(),
Comment = item.Comment,
CommentDeny = item.CommentDeny,
LoginProject = new LoginProjectDTO()
{
Project = new ProjectDTO()
{
Title = item.LoginProject.Project.Title
}
}
});
}
return returnList;
}
}
如前所述,我一直使用单例模式,但从来没有有过这个错误。是什么原因造成的,为什么?
更新:
我基本上是这样做的(下面的代码),因为这样可以解决问题。现在我更好奇是什么导致了错误。
Service.cs
using (var db = new HourRegistrationEntities())
确保将此代码作为构造函数:
public HourRegistrationEntities()
: base("ConnectionStringName")
{
}
然后在App.config
/web.config
中定义一个名为ConnectionStringName
的连接字符串
这很可能会解决您的问题。
现在关于单例:你在每次调用 GetAllDeniedHoursForLogin
时创建一个新的 HourRegistrationEntities
实例,这不是单例模式,它是工作单元模式,这是 EntityFramework 的最佳实践。继续使用它并忘记单例 DbContext,除非你真的知道你在做什么。随着 EF 跟踪实体及其关系,长期存在的单例 DbContext 会随着时间的推移变慢。还会有很多其他奇怪的问题,比如未保存的事务,副作用和许多其他难以调试的问题。
它不起作用的原因是您的 using
子句在首次使用后处理您的单例实例。在那之后,它变得无用,被处置但仍然不为空。
你坚持你总是使用单例并且它总是有效的事实并没有任何意义。将单例用于数据上下文被认为是一个非常糟糕的习惯,会导致许多问题,包括内存、并发和事务问题。
我的猜测是,之前您总是在单线程桌面应用程序上工作,其中单例仍然有风险但不会导致直接问题。然而,在 Web 服务的并发世界中,它根本行不通。
另一方面,每次 wcf 调用创建一个新实例是完全有效的,不同的实例不会干扰,您可以在使用后正确处理它们。
我可以建议您使用 "lock" 功能来访问您的单例对象 :
public sealed class XModelInstance
{
private static XDBEntities edmx = null;
private static readonly object padlock = new object();
private XModelInstance() { }
public static XDBEntities Edmx
{
get
{
lock (padlock)
{
if (edmx == null)
{
edmx = new XDBEntities();
}
return edmx;
}
}
}
}
这将避免并发访问您的上下文。
当然,不要再使用 "using" 子句来访问您的上下文 :)
调用示例:
var dbHours = XModelInstance.Edmx.HourRegistration.Where(...);
我正在尝试使用 Entity Framework 向我的 DbContext 添加单例模式。我一直为此使用单例模式,之前从未遇到过此错误。 我知道单例是最好的做法 (显然不是), 但是如果你们有空的话,能不能请解释为什么单例是最佳实践?
问题
除此之外,我收到此错误:
The underlying provider failed on open
让我们看看我的代码
DAO.cs
public class DAO
{
private static HourRegistrationEntities hourRegInstance;
public static HourRegistrationEntities HourRegInstance { get { return hourRegInstance = hourRegInstance ?? new HourRegistrationEntities(); } }
}
Service.cs(示例方法)
/// <summary>
/// Return a list of all denied Hour Registration for the Login with the given stringId
/// </summary>
/// <param name="stringId"></param>
/// <returns>A list of HourRegistrationDTO</returns>
public List<HourRegistrationDTO> GetAllDeniedHoursForLogin(string stringId)
{
var id = Int32.Parse(stringId);
using (var db = DAO.HourRegInstance)
{
var dbHours = db.HourRegistration.Where(x => x.LoginProject.Login.ID == id && x.Denied == true).ToList();
var returnList = new List<HourRegistrationDTO>();
foreach (var item in dbHours)
{
returnList.Add(new HourRegistrationDTO()
{
Id = item.ID,
Hours = item.Hours,
Date = item.Date.ToShortDateString(),
Comment = item.Comment,
CommentDeny = item.CommentDeny,
LoginProject = new LoginProjectDTO()
{
Project = new ProjectDTO()
{
Title = item.LoginProject.Project.Title
}
}
});
}
return returnList;
}
}
如前所述,我一直使用单例模式,但从来没有有过这个错误。是什么原因造成的,为什么?
更新:
我基本上是这样做的(下面的代码),因为这样可以解决问题。现在我更好奇是什么导致了错误。
Service.cs
using (var db = new HourRegistrationEntities())
确保将此代码作为构造函数:
public HourRegistrationEntities()
: base("ConnectionStringName")
{
}
然后在App.config
/web.config
中定义一个名为ConnectionStringName
这很可能会解决您的问题。
现在关于单例:你在每次调用 GetAllDeniedHoursForLogin
时创建一个新的 HourRegistrationEntities
实例,这不是单例模式,它是工作单元模式,这是 EntityFramework 的最佳实践。继续使用它并忘记单例 DbContext,除非你真的知道你在做什么。随着 EF 跟踪实体及其关系,长期存在的单例 DbContext 会随着时间的推移变慢。还会有很多其他奇怪的问题,比如未保存的事务,副作用和许多其他难以调试的问题。
它不起作用的原因是您的 using
子句在首次使用后处理您的单例实例。在那之后,它变得无用,被处置但仍然不为空。
你坚持你总是使用单例并且它总是有效的事实并没有任何意义。将单例用于数据上下文被认为是一个非常糟糕的习惯,会导致许多问题,包括内存、并发和事务问题。
我的猜测是,之前您总是在单线程桌面应用程序上工作,其中单例仍然有风险但不会导致直接问题。然而,在 Web 服务的并发世界中,它根本行不通。
另一方面,每次 wcf 调用创建一个新实例是完全有效的,不同的实例不会干扰,您可以在使用后正确处理它们。
我可以建议您使用 "lock" 功能来访问您的单例对象 :
public sealed class XModelInstance
{
private static XDBEntities edmx = null;
private static readonly object padlock = new object();
private XModelInstance() { }
public static XDBEntities Edmx
{
get
{
lock (padlock)
{
if (edmx == null)
{
edmx = new XDBEntities();
}
return edmx;
}
}
}
}
这将避免并发访问您的上下文。 当然,不要再使用 "using" 子句来访问您的上下文 :)
调用示例:
var dbHours = XModelInstance.Edmx.HourRegistration.Where(...);