如何在ServiceStack.net中使用Funq注册多个IDbConnectionFactory实例

时间:2012-01-06 20:35:01

标签: c# servicestack inversion-of-control ioc-container funq

您如何在Funq中注册不同的IDbConnectionFactory实例,然后直接在您的服务中访问它们?命名实例会以某种方式在这里发挥作用吗?

这是跨服务使用不同数据库时最好的方法吗?

谢谢!

修改

一个例子;)。我可以离开这里,因为我对IoC很新,但是比如说我有2个独立的数据库连接,我想注入。在ServiceStack中,这是在Global.asax中完成的。

container.Register<IDbConnectionFactory>(c =>
            new OrmLiteConnectionFactory(@"Connection String 1", SqlServerOrmLiteDialectProvider.Instance));                                             

container.Register<IDbConnectionFactory>(c =>
            new OrmLiteConnectionFactory(@"Connection String 2", SqlServerOrmLiteDialectProvider.Instance));                

这两个似乎都被注入了骄傲的海鲂。

然后在服务端通过以下方式自动访问它们:

public IDbConnectionFactory DbFactory { get; set; }

在这种情况下,它似乎给了我第一个注册的。如何访问服务端的特定访问权限?希望这会让它更加清晰。

这是ServiceStack.Examples中仅使用1个IDbConnectionFactory的完整示例: Movies Rest

5 个答案:

答案 0 :(得分:14)

我上面的问题仍然有效,但无论如何,以下内容可能会对您有所帮助。

Funq不支持自动构造函数注入(a.k.a. auto wiring),您必须通过构造Func<T> lambda表达式手动完成此操作。因为您已经手动执行构造函数注入,所以很容易选择要注入服务的IDbConnectionFactory。例如:

IDbConnectionFactory yellowDbConFactory =
    new YellowDbConnectionFactory();

IDbConnectionFactory blueDbConFactory =
    new BlueDbConnectionFactory();

IDbConnectionFactory purpleDbConFactory =
    new PurpleDbConnectionFactory();

container.Register<IService1>(c =>
    new Service1Impl(yellowDbConFactory,
        c.Resolve<IDep1>());

container.Register<IService2>(c =>
    new Service2Impl(blueDbConFactory);

container.Register<IService3>(c =>
    new Service3Impl(purpleDbConFactory, 
        c.Resolve<IDep2>());

当然,您也可以使用命名注册,如下所示:

container.Register<IDbConnectionFactory>("yellow",
    new YellowDbConnectionFactory());

container.Register<IDbConnectionFactory>("blue",
    new BlueDbConnectionFactory());

container.Register<IDbConnectionFactory>("purple",
    new PurpleDbConnectionFactory());

container.Register<IService1>(c =>
    new Service1Impl(
        c.Resolve<IDbConnectionFactory>("yellow"),
        c.Resolve<IDep1>());

container.Register<IService2>(c =>
    new Service2Impl(
        c.Resolve<IDbConnectionFactory>("blue"));

container.Register<IService3>(c =>
    new Service3Impl(
        c.Resolve<IDbConnectionFactory>("purple"), 
        c.Resolve<IDep2>());

由于缺乏对自动布线的支持,你最终会得到这些相当尴尬的注册,这很快就会导致你的作文根维护噩梦,但这与你的问题无关; - )< / p>

您通常应该尽量避免注册时出现歧义。在你的情况下,你有一个单独的接口,它做两件事(连接到两个数据库)。除非两个数据库共享完全相同的模型,否则每个数据库都应该拥有自己的接口(如果这两个实现不可互换,则会违反Liskov substitution principle):

interface IYellowDbConnectionFactory : IDbConnectionFactory
{
}

interface IPurpleDbConnectionFactory : IDbConnectionFactory
{
}

由于ServiceStack的工作方式,您可能需要为每个实现一个实现:

class YellowDbConnectionFactory : OrmLiteConnectionFactory,
    IYellowDbConnectionFactory
{
    public YellowDbConnectionFactory(string s) : base(s){}
}

class PurpleDbConnectionFactory : OrmLiteConnectionFactory,
    IPurpleDbConnectionFactory 
{
    public YellowDbConnectionFactory(string s) : base(s){}
}

现在您应该更改服务的定义以使用特定界面,而不是使用IDbConnectionFactory

public class MovieService : RestServiceBase<Movie>
{
    private readonly IYellowDbConnectionFactory dbFactory;

    public MovieService(IYellowDbConnectionFactory factory)
    {
        this.dbFactory = factory;
    }
}

请注意,此类现在使用构造函数注入而不是属性注入。您可以使用属性注入来实现此功能,但通常最好使用构造函数注入。这是关于它的SO question

使用Funq,您的配置将如下所示:

container.Register<MovieService>(c =>
    new MovieService(
        c.Resolve<IYellowDbConnectionFactory>());

这两个新接口和两个类并且更改为MovieService并没有赢得很多,因为Funq不支持自动布线。你将成为手动连接所有东西的人。但是,当您切换到 支持自动连接的框架时,此设计允许容器注入正确的依赖项而不会出现问题,因为没有关于要注入的内容的讨论。

答案 1 :(得分:11)

虽然Funq不支持自动布线,但ServiceStack实现了它。最新版本的ServiceStack包含Funq.Container重载:

container.RegisterAutoWired<T>();
container.RegisterAutoWiredAs<T,TAs>();
container.RegisterAs<T,TAs>();

所以在史蒂文的例子中你也可以这样做:

container.RegisterAs<YellowDbConnectionFactory,IYellowDbConnectionFactory>();

它会自动为您注册依赖项。

答案 2 :(得分:3)

我以为我在这里筹码2美分,但我意识到这个问题很老了。我想从ServiceStack访问事务数据库和日志数据库,这就是我最终从AppHostBase Configure()方法中做到的:

            container.Register<IDbConnectionFactory>(
                c => {
                    OrmLiteConnectionFactory dbFactory = new OrmLiteConnectionFactory(ConfigurationManager.ConnectionStrings["MyTransactionalDB"].ConnectionString, MySqlDialect.Provider);
                    dbFactory.ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current);
                    dbFactory.RegisterConnection("LoggingDB", ConfigurationManager.ConnectionStrings["MyLoggingDB"].ConnectionString, MySqlDialect.Provider);

                    return dbFactory;
                });

默认情况下,从工厂打开连接时会使用“MyTransactionDB”,但我可以通过以下方式显式访问服务中的日志数据库:

        using (var db = DbFactory.Open("LoggingDB"))
        {
            db.Save(...);
        }

答案 3 :(得分:1)

尝试使用Repository模式而不是使用此IoC(这会使事情不必要地复杂化)。上面的代码似乎不起作用。怀疑事情发生了变化。我还不清楚注册IDbConnectionFactory如何神奇地填充IDbConnection属性。会喜欢这个解释。 如果有人使用ServiceStack IoC容器确实可以使用它,那么我很乐意看到它。更新SS文档会非常有益(我非常乐意这样做)

答案 4 :(得分:1)

您也可以使用字典

使用您的数据库密钥名称创建一个枚举

public enum Database
    {
        Red,
        Blue
    }

在Startup.cs中,创建一个函数字典,打开一个新的SqlConnection,然后将依赖字典作为Singleton注入

Dictionary<Database, Func<IDbConnection>> connectionFactory = new()
   {
      { Database.Red, () => new SqlConnection(Configuration.GetConnectionString("RedDatabase")) },
      { Database.Blue, () => new SqlConnection(Configuration.GetConnectionString("BlueDatabase")) }
   };
services.AddSingleton(connectionFactory);

在您可以像这样获得对对象构造函数的依赖的实例之后:

public class ObjectQueries
{
   private readonly IDbConnection _redConnection;
   private readonly IDbConnection _blueConnection;

   public ObjectQueries(Dictionary<Database, Func<IDbConnection>> connectionFactory)
   {
      _redConnection = connectionFactory[Database.Red]();
      _blueConnection = connectionFactory[Database.Blue]();
   }
}

它干净且可读;)