简体   繁体   English

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

[英]How to register multiple IDbConnectionFactory instances using Funq in ServiceStack.net

How would you go about registering diferent IDbConnectionFactory instances in Funq and then access them directly within your services?您将如何在 Funq 中注册不同的 IDbConnectionFactory 实例,然后直接在您的服务中访问它们? Do named instances somehow come into play here?命名实例是否以某种方式在这里发挥作用?

Is this the best approach to take when using different databases across services?这是在跨服务使用不同数据库时采取的最佳方法吗?

Thanks!谢谢!

EDIT:编辑:

An example ;).一个例子 ;)。 I could be way off here because I'm pretty new to IoC, but say for example I have 2 separate database connections that I'd like to inject.我可能会离开这里,因为我对 IoC 还很陌生,但是例如,我想注入 2 个独立的数据库连接。 In ServiceStack, this is done in the Global.asax.在 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));                

Both of these seem to be injected honky dory.这两个似乎都注入了honky dory。

These are then accessed automatically on the service end via something like this:然后通过如下方式在服务端自动访问这些:

public IDbConnectionFactory DbFactory { get; set; }

In this case, it seems to be giving me the first one registered.在这种情况下,它似乎给了我第一个注册的。 How can I get access to a specific one on the service end?如何访问服务端的特定服务? Hopefully that makes it a little more clear.希望这让它更清楚一点。

Here's a full fledged example from ServiceStack.Examples that only uses 1 IDbConnectionFactory: Movies Rest这是来自 ServiceStack.Examples 的一个完整示例,它仅使用 1 个 IDbConnectionFactory: Movies Rest

My question above is still valid, but the following might help you anyway.我上面的问题仍然有效,但以下内容可能对您有所帮助。

Funq does not support automatic constructor injection (aka auto wiring), and you will have to do this by hand by constructing Func<T> lambda expressions. Funq 不支持自动构造函数注入(也称为自动连接),您必须通过构造Func<T> lambda 表达式来手动执行此操作。 Because you are already doing constructor injection by hand, it is easy to choose what IDbConnectionFactory you wish to inject into your services.因为您已经在手动进行构造函数注入,所以很容易选择您希望注入到您的服务中的IDbConnectionFactory Example:例子:

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>());

Of course you can also used named registrations, like this:当然你也可以使用命名注册,像这样:

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>());

Because of the lack of support for auto-wiring, you'll end up with these rather awkward registrations, and this will pretty soon result in a maintenance nightmare of your composition root, but that's unrelated to your question ;-)由于缺乏对自动连接的支持,您最终会遇到这些相当尴尬的注册,这很快就会导致您的作文根目录的维护噩梦,但这与您的问题无关;-)

You should usually try to prevent ambiguity in your registration.您通常应该尽量避免在您的注册中出现歧义。 In your case you've got a single interface, that does two things (connects to two databases).在您的情况下,您有一个接口,它可以做两件事(连接到两个数据库)。 Unless both database share the exact same model, each database deserves its own interface (if the two implementations are not interchangable, you'll be violating the Liskov substitution principle ):除非两个数据库共享完全相同的模型,否则每个数据库都应该拥有自己的接口(如果两个实现不可互换,您将违反Liskov 替换原则):

interface IYellowDbConnectionFactory : IDbConnectionFactory
{
}

interface IPurpleDbConnectionFactory : IDbConnectionFactory
{
}

Because of the way ServiceStack works, you probably need to implement an implementation for each:由于 ServiceStack 的工作方式,您可能需要为每个实现一个实现:

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

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

Now you should change the definition of your services to use the specific interface instead of using the IDbConnectionFactory :现在您应该更改服务的定义以使用特定接口而不是使用IDbConnectionFactory

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

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

Note that this class now uses constructor injection instead of property injection.请注意,此类现在使用构造函数注入而不是属性注入。 You can get this to work with property injection, but it is usually better to go with constructor injection.你可以让它与属性注入一起工作,但通常最好使用构造函数注入。 Here is a SO question about it.这是一个关于它的问题

With Funq, your configuration will then look like this:使用 Funq,您的配置将如下所示:

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

Those two new interfaces and two classes and change to the MovieService didn't win you a lot, because Funq doesn't support auto-wiring.这两个新接口和两个类以及对MovieService更改并没有让您满意,因为 Funq 不支持自动装配。 You will be the one who is wiring everything together manually.您将是手动将所有内容连接在一起的人。 However, when you switch to a framework that does support auto-wiring, this design allows the container to inject the right dependencies without a problem, because there is no discussion about what to inject.然而,当你切换到一个框架,它支持自动布线,这样的设计允许容器没有问题注入合适的依赖关系,因为没有什么注入讨论。

Although Funq doesn't support Auto wiring, ServiceStack implementation of it does.虽然 Funq 不支持自动布线,但它的ServiceStack实现支持。 The latest version of ServiceStack includes the Funq.Container overloads:最新版本的 ServiceStack 包括 Funq.Container 重载:

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

So in Steven's example you can also do:所以在史蒂文的例子中,你也可以这样做:

container.RegisterAs<YellowDbConnectionFactory,IYellowDbConnectionFactory>();

And it will automatically register the dependencies for you.它会自动为您注册依赖项。

Thought I'd chip in my 2 cents here, though I realise the question is pretty old.我以为我会在这里花 2 美分,但我意识到这个问题很老了。 I wanted to access a transactional DB and a logging DB from ServiceStack and this is how I ended up doing it from the AppHostBase Configure() method:我想从 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;
                });

By default, the "MyTransactionalDB" is used when opening a connection from the factory, but I can explicitly access the logging DB from a service via:默认情况下,在从工厂打开连接时使用“MyTransactionalDB”,但我可以通过以下方式从服务显式访问日志记录数据库:

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

Try using the Repository pattern instead of this IoC (which just complicates things unnecessarily).尝试使用 Repository 模式而不是这个 IoC(这只会使事情不必要地复杂化)。 The code above seems not to work.上面的代码似乎不起作用。 Suspect something has changed.怀疑有什么变了。 I'm still unclear as to how registering an IDbConnectionFactory magically populates the IDbConnection property.我仍然不清楚注册 IDbConnectionFactory 如何神奇地填充 IDbConnection 属性。 Would love some explanation around this.希望对此有一些解释。 If someone ever does get this working using the ServiceStack IoC container.. then I'd love to see how.如果有人确实使用 ServiceStack IoC 容器完成了这项工作..那么我很想看看如何。 And it would be hugely benefitial to update the SS docs (I'm quite happy to do it)更新 SS 文档将非常有益(我很高兴这样做)

You can also use a dictionnary您也可以使用字典

Create a enum with your database Key Name使用您的数据库密钥名称创建一个枚举

public enum Database
    {
        Red,
        Blue
    }

In Startup.cs, create a dictionary of function that open a new SqlConnection, then inject the dependency dictionary as Singleton在 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);

After you can get the instance od the dependency on object constructor like so:在您获得实例 od 之后,对对象构造函数的依赖如下:

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]();
   }
}

It's clean and readable ;)它干净且可读;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM