Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/281.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 模型类(实体)中的依赖项注入_C#_Dependency Injection_Asp.net Core - Fatal编程技术网

C# 模型类(实体)中的依赖项注入

C# 模型类(实体)中的依赖项注入,c#,dependency-injection,asp.net-core,C#,Dependency Injection,Asp.net Core,我首先用实体框架代码构建一个ASP.NET核心MVC应用程序。 我选择实现一个简单的存储库模式,为我创建的所有模型类提供基本的CRUD操作。 我选择遵循中提供的所有建议,DI就是其中之一 在.NET5中,依赖项注入对于我们不直接实例化的任何类都非常有效(例如:控制器、数据存储库等等) 我们只需通过构造函数注入它们,并在应用程序的启动类中注册映射: // Some repository class public class MyRepository : IMyRepository { pr

我首先用实体框架代码构建一个ASP.NET核心MVC应用程序。 我选择实现一个简单的存储库模式,为我创建的所有模型类提供基本的CRUD操作。 我选择遵循中提供的所有建议,DI就是其中之一

在.NET5中,依赖项注入对于我们不直接实例化的任何类都非常有效(例如:控制器、数据存储库等等)

我们只需通过构造函数注入它们,并在应用程序的启动类中注册映射:

// Some repository class
public class MyRepository : IMyRepository
{
    private readonly IMyDependency _myDependency;
    public MyRepository(IMyDependency myDependency)
    {
        _myDependency = myDependency;
    }
}

// In startup.cs :
services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyRepository, MyRepository>();
//某个存储库类
公共类MyRepository:IMyRepository
{
私有只读依赖关系_myDependency;
公共MyRepository(IMyDependency myDependency)
{
_myDependency=myDependency;
}
}
//在startup.cs中:
services.addScope();
services.addScope();
我遇到的问题是,在我的一些模型类中,我希望注入我声明的一些依赖项

但是我认为我不能使用构造函数注入模式,因为模型类通常是显式实例化的,因此,我需要为自己提供依赖项,而我不能

所以我的问题是:除了构造函数注入之外,还有其他方法注入依赖项吗?如何注入?例如,我在想一个属性模式或类似的东西

除了构造函数注入,还有其他方法注入依赖项吗?如何注入依赖项

答案是“否”,这不能用“依赖注入”来完成。但是,“是的”,您可以使用“服务定位器模式”来实现您的最终目标

您可以使用下面的代码来解析依赖项,而无需使用构造函数注入或
FromServices
属性。另外,您可以
new
根据自己的需要创建一个类的实例,它仍然可以工作——假设您已经在
Startup.cs
中添加了依赖项

public class MyRepository : IMyRepository
{
    public IMyDependency { get; } =
        CallContextServiceLocator.Locator
                                 .ServiceProvider
                                 .GetRequiredService<IMyDependency>();
}
公共类MyRepository:IMyRepository
{
公共imydependence{get;}=
CallContextServiceLocator.Locator
.服务提供者
.GetRequiredService();
}

CallContextServiceLocator.Locator.ServiceProvider
是全球服务提供商,一切都在这里。实际上不建议使用此选项。但如果你别无选择,你可以。建议始终使用DI,不要手动实例化对象,即:。;避免
new

正如我在评论中已经解释的那样,当使用
new
创建对象时,依赖项注入框架中没有任何内容涉及到该过程。因此,DI框架不可能神奇地将东西注入该对象,它只是不知道而已

由于让DI框架创建模型实例(模型不是依赖项)没有任何意义,因此如果希望模型具有依赖项,则必须显式地传递依赖项。如何做到这一点在一定程度上取决于您的模型用于什么,以及这些依赖关系是什么

简单明了的情况是让您的模型期望对构造函数的依赖。这样,如果您不提供它们,那么这是一个编译时错误,并且模型可以立即访问它们。因此,无论上面是什么,创建模型都需要具有模型类型所需的依赖关系。但在这个级别上,这很可能是一个服务或控制器,它可以访问DI,并且可以请求依赖项本身

当然,根据依赖项的数量,这可能会变得有点复杂,因为您需要将它们全部传递给构造函数。因此,另一种选择是使用一些“模型工厂”来负责创建模型对象。另一种选择是使用,将
IServiceCollection
传递给模型,然后模型可以请求它需要的任何依赖项。请注意,这通常是一种不好的做法,不再是真正的控制反转

这两种想法都有一个问题,即它们修改了对象的创建方式。一些模型,尤其是实体框架处理的模型,需要一个空构造函数,以便EF能够创建对象。因此,在这一点上,您可能会遇到一些模型的依赖关系没有得到解决的情况(并且您没有简单的方法来判断)

一种通常更好的方法,也是更明确的方法,是在需要依赖项的地方传递依赖项,例如,如果模型上有某种方法计算某些内容,但需要某些配置,则让该方法需要该配置。这也使得测试方法更容易


另一个解决方案是将逻辑移出模型。例如,他们真的很笨。他们什么也不做。所有逻辑都是在中完成的,它是一个服务,因此可以具有服务依赖关系。

在域驱动设计(具体来说是富域模型)中经常使用的模式是将所需的服务传递到您正在调用的方法中

例如,如果您想要计算增值税,您需要将增值税服务传递到
CalculateVat
方法中

在您的模型中

    public void CalculateVat(IVatCalculator vatCalc) 
    {
        if(vatCalc == null)
            throw new ArgumentNullException(nameof(vatCalc));

        decimal vatAmount = vatcalc.Calculate(this.TotalNetPrice, this.Country);
        this.VatAmount = new Currency(vatAmount, this.CurrencySymbol);
    }
你的服务级别

    // where vatCalculator is an implementation IVatCalculator 
    order.CalculateVat(vatCalculator);
最后,您的服务可以注入另一个服务,如存储库,它将获取某个国家的税率

public class VatCalculator : IVatCalculator
{
    private readonly IVatRepository vatRepository;

    public VatCalculator(IVatRepository vatRepository)
    {
        if(vatRepository == null)
            throw new ArgumentNullException(nameof(vatRepository));

        this.vatRepository = vatRepository;
    }

    public decimal Calculate(decimal value, Country country) 
    {
        decimal vatRate = vatRepository.GetVatRateForCountry(country);

        return vatAmount = value * vatRate;
    }
}

内置模型绑定器抱怨无法找到默认的构造函数。因此,您需要一个自定义的

您可能会找到类似问题的解决方案,该解决方案检查已注册的服务以创建模型

需要注意的是,下面的代码片段提供了稍微不同的功能,希望能够满足您的特定需求。下面的代码要求模型使用ctor注入。当然,这些型号都有惯用的pro
    public class DiModelBinder : ComplexTypeModelBinder
    {
        public DiModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders) : base(propertyBinders)
        {
        }

        /// <summary>
        /// Creates the model with one (or more) injected service(s).
        /// </summary>
        /// <param name="bindingContext"></param>
        /// <returns></returns>
        protected override object CreateModel(ModelBindingContext bindingContext)
        {
            var services = bindingContext.HttpContext.RequestServices;
            var modelType = bindingContext.ModelType;
            var ctors = modelType.GetConstructors();
            foreach (var ctor in ctors)
            {
                var paramTypes = ctor.GetParameters().Select(p => p.ParameterType).ToList();
                var parameters = paramTypes.Select(p => services.GetService(p)).ToArray();
                if (parameters.All(p => p != null))
                {
                    var model = ctor.Invoke(parameters);
                    return model;
                }
            }

            return null;
        }
    }
public class DiModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null) { throw new ArgumentNullException(nameof(context)); }

        if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
        {
            var propertyBinders = context.Metadata.Properties.ToDictionary(property => property, context.CreateBinder);
            return new DiModelBinder(propertyBinders);
        }

        return null;
    }
}
services.AddMvc().AddMvcOptions(options =>
{
    // replace ComplexTypeModelBinderProvider with its descendent - IoCModelBinderProvider
    var provider = options.ModelBinderProviders.FirstOrDefault(x => x.GetType() == typeof(ComplexTypeModelBinderProvider));
    var binderIndex = options.ModelBinderProviders.IndexOf(provider);
    options.ModelBinderProviders.Remove(provider);
    options.ModelBinderProviders.Insert(binderIndex, new DiModelBinderProvider());
});
public class MyModel 
{
    private readonly IMyRepository repo;

    public MyModel(IMyRepository repo) 
    {
        this.repo = repo;
    }

    ... do whatever you want with your repo

    public string AProperty { get; set; }

    ... other properties here
}
public interface IServiceProviderProxy
{
    T GetService<T>();
    IEnumerable<T> GetServices<T>();
    object GetService(Type type);
    IEnumerable<object> GetServices(Type type);
}
public static class ServiceLocator
{
    private static IServiceProviderProxy diProxy;

    public static IServiceProviderProxy ServiceProvider => diProxy ?? throw new Exception("You should Initialize the ServiceProvider before using it.");

    public static void Initialize(IServiceProviderProxy proxy)
    {
        diProxy = proxy;
    }
}
public class HttpContextServiceProviderProxy : IServiceProviderProxy
{
    private readonly IHttpContextAccessor contextAccessor;

    public HttpContextServiceProviderProxy(IHttpContextAccessor contextAccessor)
    {
        this.contextAccessor = contextAccessor;
    }

    public T GetService<T>()
    {
        return contextAccessor.HttpContext.RequestServices.GetService<T>();
    }

    public IEnumerable<T> GetServices<T>()
    {
        return contextAccessor.HttpContext.RequestServices.GetServices<T>();
    }

    public object GetService(Type type)
    {
        return contextAccessor.HttpContext.RequestServices.GetService(type);
    }

    public IEnumerable<object> GetServices(Type type)
    {
        return contextAccessor.HttpContext.RequestServices.GetServices(type);
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpContextAccessor();
    services.AddSingleton<IServiceProviderProxy, HttpContextServiceProviderProxy>();
    .......
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider sp)
{
    ServiceLocator.Initialize(sp.GetService<IServiceProviderProxy>());
}
public class FakeModel
{
    public FakeModel(Guid id, string value)
    {
        Id = id;
        Value = value;
    }

    public Guid Id { get; }
    public string Value { get; private set; }

    public async Task UpdateAsync(string value)
    {
        Value = value;
        var mediator = ServiceLocator.ServiceProvider.GetService<IMediator>();
        await mediator.Send(new FakeModelUpdated(this));
    }
}
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<SomeDbContext>(options =>
        {
            options.UseSqlServer(Configuration.GetSection("Databases").GetSection("SomeDb")["ConnectionString"]);
            options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
        }, ServiceLifetime.Scoped);

        services.AddMvcCore().AddNewtonsoftJson();
        services.AddControllersWithViews();
    }

    public async void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider provider)
    {
        ...
        IServiceScope scope = provider.CreateScope();
        SomeDbContext context = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
        SomeModelProxyClass example = new SomeModelProxyClass(context);
        await example.BuildDefaults(
            Configuration.GetSection("ProfileDefaults").GetSection("Something"),
            Configuration.GetSection("ProfileDefaults").GetSection("SomethingSomething"));
        scope.Dispose();
    }
public interface IMyRepository
{
    string WriteMessage(string input);
}

public interface IMyDependency
{
    string GetTimeStamp();
}

public class MyDependency : IMyDependency
{
    public MyDependency()
    {

    }

    public string GetTimeStamp()
    {
        return DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();
    }
}

public class MyRepository : IMyRepository
{
    private readonly IMyDependency _myDependency;
    public MyRepository(IMyDependency myDependency)
    {
        _myDependency = myDependency;
    }

    public string WriteMessage(string input)
    {
        return input + " - " + _myDependency.GetTimeStamp();
    }
}
    public ContextCRUD(DbContext context, IServiceProvider provider)
    {
        Context = context;
        Provider = provider;

        var scope = provider.CreateScope();
        var dep1 = scope.ServiceProvider.GetService<IMyRepository>();
        string msg = dep1.WriteMessage("Current Time:");
        scope.Dispose();
    }
[Route("api/[controller]")]
public class GenericController<T> : Controller where T: DbContext
{
    T Context { get; set; }
    ContextCRUD CRUD { get; set; }
    IConfiguration Configuration { get; set; }

    public GenericController(T context, IConfiguration configuration, IServiceProvider provider)
    {
        Context = context;
        CRUD = new ContextCRUD(context, provider);
        Configuration = configuration;
    }
    ...