【问题标题】:ASP.NET MVC Design Pattern Best Practice with ServicesASP.NET MVC 设计模式与服务的最佳实践
【发布时间】:2013-07-08 15:34:33
【问题描述】:

我有一个 ASP.NET MVC 3 应用程序。

我有一个ModelViewModelViewController

我使用Ninject 作为 IoC。

我的Controller 使用ViewModel 将数据传递给View

我已经开始使用Services(具体和接口类型)从ViewModel 中获取信息,并针对数据库查询它以进行操作。

我可以使用相同的Service 来设置ViewModel 吗?还是这违背了设计模式的本质?

即我可以在Service 层抽象设置ViewModel 吗?

场景

场景是;我的Model 有很多对其他Models 的引用,所以当我在控制器中设置ViewModel 时它很冗长,我觉得Controller 做得太多了。所以我希望能够做类似的事情:

var vm = _serviceProvider.SetupViewModel(Guid model1Id, Guid model2Id, /*etc..*/)

ServiceProvider 中的 SetupViewModel 函数如下所示:

public MyModelViewModel SetupViewModel(Guid model1Id, Guid model2Id, /*etc...*/)
{
    var vm = new MyModelViewModel();
    var model1 = _repository.Model1s.FirstOrDefault(x => x.Id.Equals(model1Id));
    var model2 = _repository.Model2s.FirstOrDefault(x => x.Id.Equals(model2Id));
// etc....

    vm.Model1 = model1;
    vm.Model2 = model2;

    return vm;
}

通过这样做,我还可以添加一些null 条件,而不必担心让我的Controller 真的非常非常大!!

我将 1 ViewModel 用于创建/编辑操作。我不会在其他地方重复使用 ViewModel

【问题讨论】:

  • 提供更多关于您的特定架构的信息怎么样?也许是一个简短的代码示例来解释您要做什么?
  • 我已经稍微编辑了我的问题,但我没有在里面放代码
  • 听起来你在重用 ViewModel,这很糟糕。
  • @Phil 我在问题中添加了一些信息
  • 没有“正确”之类的东西。只有最能满足您客户要求的产品。 :) 对一个项目可能至关重要的东西对另一个项目可能完全没有必要。

标签: c# asp.net-mvc asp.net-mvc-3


【解决方案1】:

我会让服务层返回一个域模型并将其映射到控制器中的 ViewModel。

通过这种方式,您可以将服务方法与多个 ViewModel 一起使用,例如用于桌面和移动视图。

您可以让AutoMapper 为您完成繁重的工作或手动完成,方法是在 ViewModel 中创建一个采用领域模型的构造函数。

领域模型:

public class Customer
{
    public int Id { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string Telephone { get; set; }

    public string Email { get; set; }

    public virtual ICollection<Order> Orders { get; set; }
}

视图模型:

public class CustomerWithOrdersModel
{
    public CustomerWithOrdersModel(Customer customer)
    {
        Id = customer.Id;
        FullName = string.Format("{0}, {1}", customer.LastName, customer.FirstName);
        Orders = customer.Orders.ToList();
    }

    public int Id { get; set; }

    public string FullName { get; set; }

    public IEnumerable<Order> Orders { get; set; }
}

编辑:AutoMapper 示例:

AutoMapper 配置文件包含从 CustomerCustomerWithOrdersModel 的映射:

public class ViewModelProfile : Profile
{
    public override string ProfileName
    {
        get { return "ViewModel"; }
    }

    protected override void Configure()
    {
        CreateMap<Customer, CustomerWithOrdersModel>()
            .ForMember(dest => dest.FullName, opt => opt.MapFrom(src => string.Format("{0}, {1}", src.LastName, src.FirstName)))
            .ForMember(dest => dest.Orders, opt => opt.MapFrom(src => src.Orders.ToList()));
    }
}

Id 按约定映射。

ViewModelProfile 的扩展方法:

public static class ViewModelProfileExtensions
{
    public static CustomerWithOrdersModel ToModel(this Customer customer)
    {
        return Mapper.Map<CustomerWithOrdersModel>(customer);
    }

    public static Customer ToEntity(this CustomerWithOrdersModel customerWithOrdersModel)
    {
        return Mapper.Map<Customer>(customerWithOrdersModel);
    }
}

控制器动作:

public ActionResult Details(int customerId)
{
    Customer customer = _customerService.GetById(customerId);
    CustomerWithOrdersModel customerWithOrders = customer.ToModel();
    return View(customerWithOrders);
}

如果您创建从CustomerWithOrdersModelCustomer 的映射,则可以使用customerWithOrdersModel.ToEntity() 将其映射回域模型。

就是这样!您可以从 ViewModel 中删除带有Customer 域模型的构造函数。

【讨论】:

  • 您能举一个AutoMapper 的例子来说明为什么它更好用吗?我目前创建和使用ViewModels 的方法比您介绍的方法要好得多,当我有复杂的Business Model 时,我只想摆脱冗长的ViewModelsControllers
  • 这很酷,我将不得不开始一个全新的项目来找出使用 AutoMapper 的所有复杂性。
  • @No1_Melman 你一定要试试。您还可以创建扩展方法,如 ToEntityToModel 以将 AutoMapper 从您的控制器中抽象出来,这也可以减少代码。
  • 那么现在这是否提供了松耦合?可以“注入”吗
  • @No1_Melman 将域模型映射到 ViewModels 与耦合无关,也不必进行 Ninjected。这只是确保AutoMapper 的实现在一个地方,而不是在你的控制器上。
【解决方案2】:

如果您将视图模型作为自己的项目并在服务层中处理视图模型的映射和返回,我认为这没有任何问题。为了分离关注点,您始终可以使用另一个组件来处理映射。

【讨论】:

  • 那么这将是另一个具有具体和接口实现的服务吗?
  • 是的。这将允许您将其注入您的控制器并有助于单元测试。因为您可以独立测试服务,而不必担心每次在您的一个控制器中使用相同的方法时进行测试。在您的控制器测试中,您可以模拟该服务。
  • 我在问题中添加了一些信息,是否仍然与您的答案一致?
  • 我实现了一项服务,用于 1) 设置 ViewModel ,2) 用于从中获取数据并 Model 进行保存。它适用于 EditCreate 动作,这意味着控制器 var entityToSave = _provider.CreateEntity(vm) 中只有 1 行,这是天才。谢谢@Maess
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-27
  • 2014-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多