如何在ASP.NET Core中将依赖项注入模型中?

Eri*_*c B 4 c# asp.net asp.net-core-mvc asp.net-core

假设我有一个控制器动作,如下所示:

[HttpPost]
public async Task<IActionResult> Add([FromBody] MyModel model){
    await model.Save();
    return CreatedAtRoute("GetModel", new {id = model.Id}, model);
}
Run Code Online (Sandbox Code Playgroud)

为了开始model.Save工作,它需要一些依赖项:

public class MyModel{
    private readonly ApplicationDbContext _context;
    public MyModel(ApplicationDbContext context){
        _context = context;
    }
    public async Task Save(){
        // Do something with _context;
    }
}
Run Code Online (Sandbox Code Playgroud)

截至目前,上下文null位于MyModel的构造函数中。我该如何注射?我知道我可以将服务注入控制器并以这种方式对模型执行操作,但是如果我宁愿使用面向对象的方法而不是贫血的领域模型怎么办?根本不可能吗?

Set*_*Set 6

您应该考虑使用DTO(数据传输对象)模式来重构代码。如果简单

  • MyModel 应该仅由数据容器->包含属性/计算的属性。
  • Save()方法的逻辑应该提取到单独的类中ModelRepository,该类应该了解依赖项,例如ApplicationDbContext

     public class ModelRepository
     {
        private readonly ApplicationDbContext _context;
    
        public ModelRepository(ApplicationDbContext context)
        {
            _context = context;
        }
    
        public async Task Save(MyModel model)
        {
            // Do something with _context;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 最后,您的控制器应使用ModelRepository的实例(使用内置DI对其进行解析)来保存数据:

    [HttpPost]
    public async Task<IActionResult> Add([FromBody] MyModel model)
    {
        await _modelRepository.Save(model);
        return CreatedAtRoute("GetModel", new {id = model.Id}, model);
    }
    
    Run Code Online (Sandbox Code Playgroud)


Ale*_*tov 5

好吧,除了DTO,您还可以使用丰富的模型。您将需要一个自定义的模型活页夹,它将负责将ctor注入模型中

内置模型联编程序抱怨找不到默认的ctor。因此,您需要一个自定义的。

您可以在此处找到类似问题的解决方案,该方法检查注册的服务以创建模型。

请务必注意,以下片段提供的功能稍有不同,希望可以满足您的特定需求。下面的代码期望带有ctor注入的模型。当然,这些模型具有您可能已定义的常规属性。这些属性将完全按预期填充,因此,在使用ctor注入绑定模型时,正确的行为是额外

    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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

该资料夹将由以下人员提供:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

活页夹的注册方法如下:

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());
});
Run Code Online (Sandbox Code Playgroud)

我不太确定新的资料夹是否必须完全在同一索引下注册,您可以尝试一下。

最后,这是您如何使用它:

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
}
Run Code Online (Sandbox Code Playgroud)

由提供(已注册)服务的绑定器创建模型类,其余的模型绑定器从其通常来源提供属性值。

高温超导