将单例模式与实体框架上下文一起使用 - 底层提供程序在打开时失败

时间:2015-08-28 10:00:10

标签: c# entity-framework wcf design-patterns

我正在尝试使用Entity Framework向我的DbContext添加单例模式。我总是使用单例模式,之前从未遇到过这个错误。 我知道单身人士是最佳做法 (显然不是)但是如果你们有空闲时间,请解释为什么单身人士是最佳做法?

问题

除此之外,我收到了这个错误:

  

底层提供程序在打开时失败

让我们来看看我的代码

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())

3 个答案:

答案 0 :(得分:8)

它不起作用的原因是你的using子句在首次使用后处理你的单例实例。在那之后,它变得无用,处置但仍然不为空。

你坚持要求你总是使​​用单身人士这一事实并不总是有效。将单例用于数据上下文被认为是一种非常糟糕的习惯,会导致许多问题,包括内存,并发和事务问题。

我的猜测是,在你总是使用单线程桌面应用程序之前,单例仍然存在风险,但不会导致直接问题。然而,在这里,在Web服务的并发世界中,它将无法工作。

另一方面,每次wcf调用创建一个新实例是完全有效的,不同的实例不会干扰,并且在使用后你可以正确处理它们。

答案 1 :(得分:3)

我建议您使用“锁定”功能来访问您的单件对象:

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(...);

答案 2 :(得分:2)

确保您将此代码作为构造函数:

public HourRegistrationEntities()
    : base("ConnectionStringName")
{
}

然后在名为App.config

web.config / ConnectionStringName中定义连接字符串

这很可能会解决您的问题。 现在关于单身人士:您在每次调用HourRegistrationEntities时创建GetAllDeniedHoursForLogin的新实例,这不是单身模式,它的工作单元模式是最佳实践的EntityFramework。继续使用它并忘记单身DbContext,除非你真的知道你做了什么。由于EF跟踪实体及其关系,长期存在的单身DbContext将随着时间的推移变慢。还会有许多其他奇怪的问题,例如未保存的事务,副作用以及许多其他难以调试的问题。