【问题标题】:Proper use of Singleton in DAL and BO layer?在 DAL 和 BO 层中正确使用 Singleton?
【发布时间】:2014-06-18 13:51:12
【问题描述】:

我的任务是为一个项目实施业务对象/数据访问层,并且必须同时期待成千上万的用户。 我一直使用单例来管理 DAL,但我从来没有考虑过它会如何同时处理这么多多个用户,所以我想问问它的正确用途。

我有:

public class UserDAL
{
    private static UserDAL _userDAL = null;

    //Private constructor 
    private UserDAL() { }

    public static UserDAL GetInstance()
    {
        if(_userDAL == null)
        { _userDAL = new UserDAL(); }

        return _userDAL;
    }

    //Example of a method
    public User GetUsers()
    {
        IDataReader dataReader = ConnectionFactory.GetConnection().ExecuteSomeQuery("queryHere");
    }
}

对于我的连接工厂,我认为这不是问题,尽管我确实读过最好将连接池留给 ADO.NET 本身:

public sealed class ConnectionFactory
{
    private static string _connectionString = ConfigurationManager.ConnectionStrings["ConnectionName"].ConnectionString;

    //My connection interface
    private static IConnection _connection = null;

    public static IConnection GetConnection()
    {
        if(_connection == null)
        {
            //some checks to determine the type
            _connection = new SQLConnection(_connectionString);
        }
        return _connection;
    }
}

我也在 BO 中使用单例模式,虽然我认为没有必要:

public class UserBO
{
    private static UserBO _userBO = null;
    private static UserDAL _userDAL = null;

    private UserBO() { }

    public static UserBO GetInstance()
    {
        if(_userBO == null)
        {
            _userBO = new UserBO();
            _userDAL = UserDAL.GetInstance();
        }

        return _userDAL;
    }

    //Example of a method
    public User GetUser()
    {
        //Rules
        return _userDAL.GetUsers();
        //return UserDAL.GetInstance().GetUsers(); //or this
    }
}

我这样做只是为了可以调用 UI/Presentation 层:

User someUser = UserBO.GetInstance().GetUser(1);

这对我迄今为止制作的应用程序很有效,但我猜这是因为同时没有太多用户。 我担心当第二个用户请求某些东西但已经有第一个用户在其中执行一些繁重的操作时,UserDAL 实例中会发生什么。

我应该把这个模式放到 BO/DAL 层中,只保留在 ConnectionFactory 中吗?如果我使用它,我会遇到什么问题吗?

【问题讨论】:

    标签: c# .net design-patterns singleton


    【解决方案1】:

    我肯定会完全放弃它,尤其是对于 Connection:connectionFactory 可以是静态的,但每次询问时都会返回一个新连接:ADO.NET 非常擅长管理连接池,您只需要退出就是这样。

    在任何具有可变状态的事物中,请远离单例。这包括 ADO.NET 连接和您的实际业务对象。让一个用户改变另一个用户正在使用的对象的状态可能会导致各种奇怪的错误:在网站中,您基本上有一个大规模的多线程应用程序,而可变单例是非常糟糕的消息!

    但是,当两个或多个用户更改同一业务对象的副本时,您确实需要提出某种锁定策略。一个有效的策略包括说“实际上,这不会是一个问题,所以我会忽略它”——但前提是你已经考虑过了。两种基本策略是乐观锁定和悲观锁定。

    乐观锁定意味着您乐观地认为大多数用户不会更改相同的内容(无论出于何种原因),因此您不会将数据库锁放在读取数据上。这是网站上唯一的可能性

    悲观锁定表示所有可能更改的数据在读取时都将应用 DB 锁,直到用户完成它。这意味着保持事务处于打开状态,这对于网站来说是不切实际的。

    可以通过创建更新语句来实现乐观锁定,该语句仅更新当前用户未更改的所有列在数据库中也未更改的行;如果有,则其他人已更改同一行。或者,您可以向所有表添加一列 - version int not null - 并更新自您读取对象以来版本未更改的位置;您还可以在每次更新中增加版本号。

    如果任一方法失败,您需要重新读取当前数据并让您的用户确认或重新应用他们的更改。有点痛苦,但可能是必要的。

    【讨论】:

    • 我认为你误解了我的业务对象(或者我学错了),但这根本不应该是一个实体,它只是一个应用所有业务规则和所有调用的层经过。所以它应该返回实体的新实例(例如用户),所以 UserBO 根本没有任何状态。我肯定会为实体实施这种锁定策略,谢谢你的洞察力!
    • 是的,很抱歉。我假设 BO 是一个业务对象。看起来它是获取和更新/插入业务对象的包装器。只要它没有可变的状态就没有问题。
    【解决方案2】:

    为了可测试性,我建议您放弃单例模式:Dependency Injection & Singleton Design pattern

    相反,看看依赖注入。 Ninject 是一个很好的开始方式。

    DI 将负责将 BO 和 DAL 连接在一起:

    public interface IUserRepository
    {
         IEnumerable<User> GetUsers();
    }
    
    public class UserBO
    {
         private readonly IUserRepository _userRepository;
    
         public UserBO(IUserRepository userRepository){
             _userRepository = userRepository;
         }
    
         public IEnumerable<User> GetUsers()
         {
             return _userRepository.GetUsers();
         }
    }
    

    关于重用连接池:Should you reuse SqlConnection, SqlDataAdapter, and SqlCommand objects?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-19
      • 1970-01-01
      • 2013-05-31
      • 2014-04-08
      • 1970-01-01
      • 2011-06-05
      • 1970-01-01
      • 2013-05-31
      相关资源
      最近更新 更多