Asp.net mvc asp.net mvc控制器构造函数中的多个依赖项

Asp.net mvc asp.net mvc控制器构造函数中的多个依赖项,asp.net-mvc,model-view-controller,dependency-injection,controller,Asp.net Mvc,Model View Controller,Dependency Injection,Controller,在我的asp.net mvc控制器的构造函数中,我有多(5)个接口,它们通过以下方式与我的数据库通信: [HttpGet] public ActionResult Create() { var releases = _releaseDataProvider.GetReleases(); var templates = _templateDataProvider.GetTemplates(); var createTestplanViewModel = new Creat

在我的asp.net mvc控制器的构造函数中,我有多(5)个接口,它们通过以下方式与我的数据库通信:

[HttpGet]
public ActionResult Create()
{          
 var releases = _releaseDataProvider.GetReleases();
 var templates = _templateDataProvider.GetTemplates();
 var createTestplanViewModel = new CreateTestplanViewModel(templates, releases);
 return PartialView(createTestplanViewModel);
}
上面我使用了两个不同的接口从数据库中获取数据

业务案例:要创建一个测试计划,我需要向用户显示可用的版本+模板,用户可以从中选择

如何减少这两个接口的依赖性/过度注入

编辑

在我看来,您基本上有以下选项来减少构造函数依赖项计数:

  • 拆分控制器
  • 在两个接口前面添加图层
  • 切换到属性注入
  • 服务定位器
  • #3和#4是为了更好的度量而包含的,但是它们显然并没有减少依赖项计数,它们只是对构造函数隐藏它们。它们也有几个缺点,我认为服务定位器大部分时间都是邪恶的。

    对于#1,如果您觉得您的构造函数实际上在做两个以上的工作,并且存在一个可以拆分的干净分离,我会这样做。但是,我从您的回答中假设您已经考虑过这一点,并且不想这样做

    剩下#2-添加另一层。在本例中,这将为该特定视图模型引入工厂接口。天真地说,我会给这个命名为ICreateTestplanViewModelFactory,但如果您愿意,您可以为您的应用程序命名一些更为合理的名称。它上的单个方法将构造CreateTestplanViewModel

    这使得该视图的数据来自两个源这一事实仅仅是一个实现细节。您将连接一个将IReleaseDataProvider和ITemplateDataProvider作为构造函数依赖项的实现


    这与我的建议大致相同:

    public interface IProvideTestPlanSetupModel
    {
        CreateTestplanViewModel GetModel();
    }
    
    public class TestPlanSetupProvider : IProvideTestPlanSetupModel
    {
        private readonly IReleaseDataProvider _releaseDataProvider;
        private readonly ITemplateDataProvider _templateDataProvider;
    
        public TestPlanSetupProvider(IReleaseDataProvider releaseDataProvider, ITemplateDataProvider templateDataProvider)
        {
            _releaseDataProvider = releaseDataProvider;
            _templateDataProvider = templateDataProvider;
        }
    
        public CreateTestplanViewModel GetModel()
        {
            var releases = _releaseDataProvider.GetReleases();
            var templates = _templateDataProvider.GetTemplates();
    
            return new CreateTestplanViewModel(releases, templates);
        }
    }
    
    public class TestPlanController : Controller
    {
        private readonly IProvideTestPlanSetupModel _testPlanSetupProvider;
    
        public TestPlanController(IProvideTestPlanSetupModel testPlanSetupProvider)
        {
            _testPlanSetupProvider = testPlanSetupProvider;
        }
    
        [HttpGet]
        public ActionResult Create()
        {
            var createTestplanViewModel = _testPlanSetupProvider.GetModel();
            return PartialView(createTestplanViewModel);
        }
    }
    
    如果您不喜欢在控制器之外的任何位置构建视图模型,那么接口可以提供一个中间对象,该对象的属性与您将复制到视图模型的属性相同。但这是愚蠢的,因为这种数据组合只与特定视图相关,而该视图正是视图模型应该表示的

    另一方面,通过同一个模型进行读/写操作时,您似乎遇到了相当常见的麻烦。由于这些问题让您非常烦恼,您可能会调查CQR,这可能会让您在直接与数据库进行此类查询时感觉不那么脏,并有助于您绕过我们都非常喜欢的分层迷宫。它看起来很有希望,尽管我还没有在生产应用程序中测试驾驶它的乐趣。

    EDIT

    在我看来,您基本上有以下选项来减少构造函数依赖项计数:

  • 拆分控制器
  • 在两个接口前面添加图层
  • 切换到属性注入
  • 服务定位器
  • #3和#4是为了更好的度量而包含的,但是它们显然并没有减少依赖项计数,它们只是对构造函数隐藏它们。它们也有几个缺点,我认为服务定位器大部分时间都是邪恶的。

    对于#1,如果您觉得您的构造函数实际上在做两个以上的工作,并且存在一个可以拆分的干净分离,我会这样做。但是,我从您的回答中假设您已经考虑过这一点,并且不想这样做

    剩下#2-添加另一层。在本例中,这将为该特定视图模型引入工厂接口。天真地说,我会给这个命名为ICreateTestplanViewModelFactory,但如果您愿意,您可以为您的应用程序命名一些更为合理的名称。它上的单个方法将构造CreateTestplanViewModel

    这使得该视图的数据来自两个源这一事实仅仅是一个实现细节。您将连接一个将IReleaseDataProvider和ITemplateDataProvider作为构造函数依赖项的实现


    这与我的建议大致相同:

    public interface IProvideTestPlanSetupModel
    {
        CreateTestplanViewModel GetModel();
    }
    
    public class TestPlanSetupProvider : IProvideTestPlanSetupModel
    {
        private readonly IReleaseDataProvider _releaseDataProvider;
        private readonly ITemplateDataProvider _templateDataProvider;
    
        public TestPlanSetupProvider(IReleaseDataProvider releaseDataProvider, ITemplateDataProvider templateDataProvider)
        {
            _releaseDataProvider = releaseDataProvider;
            _templateDataProvider = templateDataProvider;
        }
    
        public CreateTestplanViewModel GetModel()
        {
            var releases = _releaseDataProvider.GetReleases();
            var templates = _templateDataProvider.GetTemplates();
    
            return new CreateTestplanViewModel(releases, templates);
        }
    }
    
    public class TestPlanController : Controller
    {
        private readonly IProvideTestPlanSetupModel _testPlanSetupProvider;
    
        public TestPlanController(IProvideTestPlanSetupModel testPlanSetupProvider)
        {
            _testPlanSetupProvider = testPlanSetupProvider;
        }
    
        [HttpGet]
        public ActionResult Create()
        {
            var createTestplanViewModel = _testPlanSetupProvider.GetModel();
            return PartialView(createTestplanViewModel);
        }
    }
    
    如果您不喜欢在控制器之外的任何位置构建视图模型,那么接口可以提供一个中间对象,该对象的属性与您将复制到视图模型的属性相同。但这是愚蠢的,因为这种数据组合只与特定视图相关,而该视图正是视图模型应该表示的


    另一方面,通过同一个模型进行读/写操作时,您似乎遇到了相当常见的麻烦。由于这些问题让您非常烦恼,您可能会调查CQR,这可能会让您在直接与数据库进行此类查询时感觉不那么脏,并有助于您绕过我们都非常喜欢的分层迷宫。它看起来很有希望,尽管我还没有在生产应用程序中测试驱动它的乐趣。

    一个很好的解耦数据库的通用方法是使用一个工作单元。这是一篇关于的好文章,也是另一篇关于的文章

    总之,您可以在所有数据库/服务调用所在的位置创建一个单元,它可以处理数据库逻辑。这将减少多个接口对单个点的依赖性,因此只需向控制器中注入一个类

    引用MSDN文章:

    根据Martin Fowler的说法,工作单元模式“维护一个列表 受业务事务影响的对象,并协调 写出更改和解决并发问题。”


    一个很好的解耦数据库的通用方法是使用一个工作单元。这是一篇关于的好文章,也是另一篇关于的文章

    总之,您可以在所有数据库/服务调用所在的位置创建一个单元,它可以处理数据库逻辑。这会将多个接口的依赖性减少到一个点,因此
    using System.Diagnostics;
    using SimpleInjector;
    
    namespace MyApp.Infrastructure
    {
        sealed class SimpleQueryProcessor : IQueryProcessor
        {
            private readonly Container _container;
    
            public SimpleQueryProcessor(Container container)
            {
                _container = container;
            }
    
            [DebuggerStepThrough]
            public TResult Execute<TResult>(IDefineQuery<TResult> query)
            {
                var handlerType = typeof(IHandleQueries<,>)
                    .MakeGenericType(query.GetType(), typeof(TResult));
    
                dynamic handler = _container.GetInstance(handlerType);
    
                return handler.Handle((dynamic)query);
            }
        }
    }