在DAL和BO层中正确使用Singleton?

时间:2014-06-18 13:51:12

标签: c# .net design-patterns singleton

我的任务是为项目实现业务对象/数据访问层,并且必须同时期待数千个用户。 我总是使用单身人士来管理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");
    }
}

对于我的Connection Factory,我不认为这是一个问题,虽然我确实认为最好将连接池留给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中吗?如果我使用它,是否会出现任何问题?

2 个答案:

答案 0 :(得分:1)

我肯定会完全删除它,特别是对于Connection:connectionFactory可能是静态的,但每次询问时都返回一个新连接:ADO.NET非常擅长管理连接池,你只需要离开它的方式。

任何具有多变状态的东西都要远离单身人士。这包括ADO.NET连接和您的实际Business Objects。让一个用户改变另一个用户正在使用的对象的状态会导致各种奇怪的错误:在一个网站中,你基本上有一个大型的多线程应用程序,可变的单例是非常坏的消息!

但是,当两个或更多用户更改同一业务对象的副本时,您确实需要提出某种锁定策略。一个有效的策略包括说“实际上,这不会成为一个问题,所以我会忽略它”#39; - 但只有你想到了它。两个基本策略是乐观和悲观锁定。

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

悲观锁定表示所有可能更改的数据在读取时都会应用DB锁,直到用户完成。这意味着保持交易开放,对网站来说不实用。

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

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

答案 1 :(得分:0)

我建议你摆脱Singleton模式的可测试性: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?