在DAL和BO层正确使用单例

本文关键字:单例 DAL BO | 更新日期: 2023-09-27 18:09:05

我的任务是为一个项目实现业务对象/数据访问层,并且必须同时预期数千个用户。我一直使用单例来管理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。网络本身:

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中保留它吗?如果我使用这个,会有什么问题吗?

在DAL和BO层正确使用单例

我肯定会完全放弃它,特别是对于连接:connectionFactory可以是静态的,但是每次请求它时返回一个新的连接:ADO。. NET非常擅长管理连接池,你只需要避开它。

在任何具有可变状态的情况下,避免使用单例。这包括ADO。. NET连接,以及实际的业务对象。让一个用户改变另一个用户正在使用的对象的状态可能会导致各种奇怪的bug:在一个网站中,你基本上有一个大规模的多线程应用程序,可变的单例是非常坏的消息!

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

乐观锁定意味着你乐观地认为大多数用户不会改变相同的东西(无论出于什么原因),所以你不把数据库锁放在读数据上。这是一个网站上唯一的可能性

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

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

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

为了可测试性,我建议你放弃单例模式:单例设计模式

让我们看看依赖注入。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();
     }
}

对于Connection Pool的重用:是否应该重用SqlConnection、SqlDataAdapter和SqlCommand对象?