实例字段和using子句中的IDbConnection

时间:2015-03-27 08:23:22

标签: c# wpf oop

我正在编写一个服务类,与底层数据库进行一些交互。我将连接作为类构造函数中的参数传递。

public class MyDbService : IDbService
{
    private readonly IDbConnection _connection;

    public MyDbService(IDbConnection connection)
    {
        _connection = connection;
    }

    public void DoStuff()
    {
        // Sql-Server implementation
        var conn = _connection as SqlConnection;
        if(conn != null)
        {
            using(conn)
            {
                conn.Open();
                // Do stuff
            }
        }

        // Implementation for other providers
        ...
    }
}

DoStuff()在第一次调用时正在运行,但是由于using()子句,conn对象被释放,当调用InvalidOperationException时我得到conn.Open()试。

我可以通过移除using并放置conn.Close()来解决问题,但这看起来像是一个丑陋的"我的选择?

所以,我有两个问题,第一个是oop非常基本的问题:

  1. 当我使用_connection时,为什么using(conn)对象被处理?
  2. 什么是"最佳实践"什么时候在构造函数中传递连接对象?
  3. 编辑:

    该类是WPF应用程序的一部分。

    由于问题似乎与DI有关,我概述了背后的课程。我知道,我需要传递一个连接对象的新实例,因为它在服务中第一次使用后被释放 - 我只是看不出如何(至少没有通过Unity容器携带到viewmodel并手动解决)。当然,一个非常简单的解决方案是将连接字符串和提供程序传递给服务构造函数,但我想让这种方法以某种方式工作。非常感谢任何提示:)

    App.xaml.cs:

    public partial class App : Application
        {
            protected override void OnStartup(StartupEventArgs e)
            {
                base.OnStartup(e);
    
                var connString = System.Configuration.ConfigurationManager.ConnectionStrings["LocalTest"].ConnectionString;
                var providerName = System.Configuration.ConfigurationManager.ConnectionStrings["LocalTest"].ProviderName;
    
                var connection = DbProviderFactories.GetFactory(providerName).CreateConnection();
    
                if (connection != null)
                {
                    connection.ConnectionString = connString;
                    var myService= new MyDbService(connection);
    
                    // Register dependency injection
                    var container = new UnityContainer();
                    container.RegisterInstance<IDbConnection>(connection);
                    container.RegisterInstance<IDbService>(myService);
    
    
                    var mainWindow = container.Resolve<MainWindow>();
                    mainWindow.Show();
                }
                else
                {
                    // Handle null connection
                }
            }
        }
    

    MainWindow.xaml.cs:

    public partial class MainWindow: Window
    {
        private MyViewModel _vm;
    
        public MainWindow()
        {
            InitializeComponent();
        }
    
        [Dependency]
        public MyViewModel ViewModel
        {
            set
            {
                _vm = value;
                DataContext = _vm;
            }
        }
    }
    

    MyViewModel.cs:

    public class MyViewModel: INotifyPropertyChanged
    {
        private readonly IDbService _dbService;
    
        public MyViewModel(IDbService dbService)
        {
            _dbService = dbService;
        }
    
        public void DoStuff()
        {
            _dbService.DoStuff();
        }
    
        ...
    
        // Other viewmodel stuff
    
    }
    

1 个答案:

答案 0 :(得分:0)

正在处理_connection,因为您将它包装在using语句中,并且您可能每次都传递相同的实例,因为它看起来像您创建了一个连接var connection = ...和然后每次通过DI传递相同的变量。然后在DoStuff方法中它被释放,但随后将相同的已处理实例传递到下一个MyDbService

当您设置DI而不是返回单个变量(即connection)时,请尝试返回DbProviderFactories.GetFactory(providerName).CreateConnection()。这样,每次从DI容器请求新的IDbConnection时,您将始终获得一个新的数据库实例。

之前我没有使用过Unity,但您应该更改container.RegisterInstance<IDbConnection>(connection);以使用接受lambda表达式的Register方法的版本,例如:

container.Register<IDbConnection>(c => DbProviderFactories.GetFactory(providerName).CreateConnection);

以下帖子使用InjectionFactory,可能会提供有关如何执行此操作的有用示例:Registering types with lambda expression

更新#1:作为附注,我不会处理连接DoStuff。而是实现IDisposable并在那里处置_connection。然后,这允许您在类的其他方法中重用连接。

更新#2:我还会在您的构造函数中执行_connection as SqlConnection,然后在_connection中存储引用。如果转换失败,则抛出异常。这可以确保在调用者继续使用类的方法/属性之前,您具有有效的连接类型。然后,您可以从DoStuff方法中删除广告。