简体   繁体   English

ASP.NET Core依赖项注入:服务在运行时使用Func委托解析

[英]ASP.NET Core dependency injection: Service resolved at runtime using a Func delegate

I'm trying to inject a service to my controllers but I want to inject a different instance of my service depending of several parameters. 我正在尝试向控制器注入服务,但是我想根据几个参数注入服务的其他实例。 Actually for this part it's working, I'm able to do it. 实际上,对于这部分来说,它是可行的,我能够做到。

What I want is to load a specific instance of IRepository<Database> based on some configuration we get from a configuration file and respecting the DRY rule (don't repeat yourself). 我想要的是根据我们从配置文件获得的某些配置并遵守DRY规则(不重复自己),加载IRepository<Database>的特定实例。

I have these 2 classes: 我有这两个课程:

public abstract class FooServicesProvider
{
    public Func<IServiceProvider, IRepository<Database>> DatabaseRepository = provider =>
    {
        return null;
    };
}

public class FooFileSystemServicesProvider : FooServicesProvider
{
    public new Func<IServiceProvider, IRepository<Database>> DatabaseRepository = provider =>
    {
        //Specific code determining which database to use and create a new one if needed
        //our databases are FOLDERS containing some files
        //knowing how chosenDb.FullName is set is not important here
        //[...]
        var databaseRepository = new DatabaseFileSystemRepository(chosenDb.FullName);
        databaseRepository.testProperty = "Foo value";
        return databaseRepository;
    };
}

Notice the new keyword used to redefine the code of my Func. 请注意用于重新定义我的Func代码的new关键字。 This is the best way I found because of the Func delegate, I'm very limited, I can't use it in an Interface neither override it. 这是我发现的最好方法,因为有Func委托,我非常有限,不能在接口中使用它,也不能覆盖它。

Now in my ConfigureServices method in Startup.cs I have this code 现在在Startup.cs的 ConfigureServices方法中,我有以下代码

var fakeConfiguration = "File";
FooServicesProvider servicesProvider = null;
if(fakeConfiguration == "File")
{
    servicesProvider = new FooFileSystemServicesProvider();
}
else
{
    servicesProvider = new AnotherFooServicesProvider();
}
//Here is the tricky part
//This should call FooFileSystemServicesProvider.DatabaseRepository because of the "new" keyword, but it's NOT
services.AddScoped<IRepository<Database>>(servicesProvider.DatabaseRepository);

My problem is that the new keyword is ignored at runtime and the executed Func is the one declared in my base class instead of the derived one. 我的问题是new关键字在运行时被忽略,执行的Func是在我的基类中声明的关键字,而不是派生的关键字。

If I do this it's working 如果我这样做,那就行得通

services.AddScoped<IRepository<Database>>((servicesProvider as FooFileSystemServicesProvider).DatabaseRepository);

But I don't want to cast it as I can't know of which type my servicesProvider will finally be. 但是我不想进行转换,因为我不知道我的servicesProvider最终将是哪种类型。

I've tried to get the type of my servicesProvider and cast it with its own type but I get compiler error because a Type variable and a Class are different. 我试图获取我的servicesProvider的类型并将其强制转换为其自己的类型,但是由于Type变量和Class不同,我遇到了编译器错误。

So how can I get the good Func executed at runtime? 那么,如何在运行时执行良好的Func? Thanks 谢谢

Ok so I finally managed to do what I want, it was actually not that hard, my main problem was to handle the fact that my Func was not a method but a delegate. 好的,所以我终于设法做自己想做的事,实际上并不难,我的主要问题是要处理我的Func不是方法而是委托的事实。 I'm not used to deal with this variable type. 我不习惯处理这种变量类型。

My code in Startup.cs remains unchanged but here is the new code of my custom ServicesProvider 我在Startup.cs中的代码保持不变,但这是我的自定义ServicesProvider的新代码

public abstract class FooServicesProvider
{
    public Func<IServiceProvider, IRepository<Database>> DatabaseRepository { get; protected set; } 
}

public class FooFileSystemServicesProvider : FooServicesProvider
{
    public FooFileSystemServicesProvider()
    {
        base.DatabaseRepository = GetDatabaseRepository;
    }

    private DatabaseFileSystemRepository GetDatabaseRepository(IServiceProvider serviceProvider)
    {
        //Specific code determining which database to use and create a new one if needed
        //our databases are FOLDERS containing some files
        //knowing how chosenDb.FullName is set is not important here
        //[...]
        var databaseRepository = new DatabaseFileSystemRepository(chosenDb.FullName);
        databaseRepository.testProperty = "Foo value";
        return databaseRepository;
    }        
}

Just in case people are wondering: DatabaseFileSystemRepository is a class that implements the interface IRepository<Database>> 以防万一,人们想知道: DatabaseFileSystemRepository是一个实现IRepository<Database>>接口的类。

If anyone comes up with a different solution, I'm very curious to know it. 如果有人提出不同的解决方案,我很好奇。

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

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