代码之家  ›  专栏  ›  技术社区  ›  Jaimal Chohan

依赖项插入SQL连接?

  •  6
  • Jaimal Chohan  · 技术社区  · 15 年前

    首先,我开始使用structuremap,但是任何DI框架中的一个例子都可以。

    我也有个班,

    public class GeoData
    {
       public List<Country> GetCountries()
       {
          IDbConnection con = new SqlConnection(ConfigurationManager.ConnectionString["GeoDataConnection"])    
          //Sql stuff to return countries from a database
       }
    }
    

    这是一个简单的关于类实际外观的观点,但本质上就是这样。

    现在,我有了一个新的要求。我需要能够在类初始化或方法上更改connectionString。例如。

    public void Do()
    {
       var geoData = new GeoData();
    
       if(x)
       {
          geoData.ConnectionString = ConfigurationManager.ConnectionString["LIVEGeoDataConnection"]);
       }
       else
       {
          geoData.ConnectionString = ConfigurationManager.ConnectionString["STAGINGGeoDataConnection"]);
       }
    
       geoData.GetCountries();
    }
    

    有没有更好的解决方案可以使用依赖注入?您将如何使用自己选择的DI框架来实现这一点?

    6 回复  |  直到 10 年前
        1
  •  5
  •   Wim    15 年前

    很简单,您不需要框架。只是为您的 GeoData 班级。

    GeoData geo = new GeoData(yourConnString);

    字符串是您的依赖项。因为它不是一个复杂的类型,所以您的依赖注入就在那里。

    DI不是火箭科学,尽管有些人可能希望你相信这一点。

        2
  •  9
  •   senfo    10 年前

    从技术上讲,wim hollebrandse已经回答了你的问题,但我只是想指出,我个人会用另一种方式来做,因为我不喜欢每次实例化类时都传递连接字符串。我知道您有一个默认的构造函数,但是我认为我们仍然可以使它更清晰一些。

    首先,我将创建一个静态类来获取您的连接,如下所示:

    public static class ConnectionFactory
    {
        public static IDbConnection GetConnection()
        {
            return GetConnection(ConfigurationManager.ConnectionString["GeoDataConnection"]);
        }
    
        public static IDbConnection GetConnection(string connectionString)
        {
            return new SqlConnection(connectionString);
        }
    }

    然后,我会这样使用它:

    public class GeoData
    {
       public List GetCountries()
       {
          using (IDbConnection con = ConnectionFactory.GetConnection())
          {
            //Sql stuff to return countries from a database
          }
       }
    }

    使用这种方法,如果默认连接字符串发生更改,您只需要更改一行代码,而不需要转到配置文件中引用连接字符串的每一行。但是,如果需要,它可以为您提供覆盖连接字符串的能力。

    希望有帮助…

        3
  •  5
  •   TrueWill    15 年前

    问自己的第一个问题是什么是地理数据?换句话说,班级的责任是什么?

    它似乎是 Domain Layer ,因此可能包含业务逻辑。它可以被( coupled 其他类别。

    如果是这样,依赖关系是什么?确定这一点的一种方法是尝试写入 unit tests 单独测试地理数据。如果测试需要重要的设置,则被测试的类要么与其他类紧密耦合,要么具有多个职责(低 cohesion )。

    假设我们更改类,以便构造函数接受一个连接字符串参数。我们如何测试public getcountries方法?首先,我们用已知的测试数据建立一个数据库…

    这既耗时又脆弱(如果有人更新数据怎么办?),测试运行相对缓慢(必须连接到数据库)。

    好吧,我们可以将实现IDBConnection的对象传递给构造函数(构造函数注入)。注意,依赖注入通常涉及传入接口或抽象类。要测试它,我们必须创建一个假的idbConnection。我们可以使用 isolation (mocking) framework . 但当调用createCommand时,我们需要它来创建一个假的idbcommand…

    引用Jeremy Miller(作者 StructureMap )“这是太多的努力,收获太少。”见他的文章。 Best and Worst Practices for Mock Objects .

    一种可能是使用 Repository Pattern . 您将把一个接口传递给特定的存储库,再传递给geodata构造函数。这将很容易伪造(手动或与模拟库)测试。具体的存储库将处理所有的数据访问。它可以与 ORM 进一步抽象数据访问的框架。连接字符串管理可以通过ORM或存储库(最好是在另一个依赖项或基类中)来完成。

    如果这听起来很复杂,那是因为它很复杂。您选择了一个最困难的依赖注入案例(不幸的是,这也是最常见的案例之一)。

    依赖注入本身是一个相当简单的概念。如果您的类正在调用Web服务,那么您可以将Web服务代码放在一个单独的类中,该类不做任何其他操作,实现一个接口,并将该接口传递给原始类。如果您有很多类和/或依赖项,那么DI/IOC容器框架可以使这变得更容易,但是它们不是一个需求。

    编辑: 只是要明确一点,依赖注入是 复杂的部分。分离数据访问是。

        4
  •  1
  •   Ron Klein Noa Kuperberg    15 年前

    我将创建一个工厂来创建 GeoData 类,该类反过来实现与 Do 方法(比如说) IDoCommand )。

    工厂有责任使用全局上下文来确定要注入到 地理数据 实例(构造器是我的首选技术),或者将其包含在 Create 方法作为参数。

        5
  •  0
  •   Kasper    15 年前

    马丁·福勒有一篇关于这个问题的文章 here 这就解释了各种方法。就个人而言,我更喜欢接口注入,但这是一个品味问题。

        6
  •  0
  •   Lee    15 年前

    我要做的是创建一个包含连接字符串选择逻辑的新类,然后使用该类获取地理数据实例的连接字符串:

    public class ConnectionStringManager
    {
        public string GeoDataConnectionString
        {
            get
            {
                return x 
                    ? ConfigurationManager.ConnectionString["LIVEGeoDataConnection"])
                    : ConfigurationManager.ConnectionString["STAGINGGeoDataConnection"]);
            }
        }
    }
    

    然后,可以将其注入包含do方法的类中,以设置如下地理数据实例:

    public class Blah(ConnectionStringManager connManager)
    {
        public void Do()
        {
            var geoData = new GeoData { ConnectionString = connManager.GeoDataConnectionString };
            geoData.GetCountries();
        }
    }