简体   繁体   English

Asp.net 核心 MVC 中 ViewModel 的适配器模式

[英]Adapter Pattern for ViewModels in Asp.net core MVC

Currently I am converting ViewModels into model objects and also the other way round.目前我正在将 ViewModels 转换为模型对象,反之亦然。 I do this in the controller and sometimes it can be quite a big object with many properties.我在控制器中执行此操作,有时它可能是一个具有许多属性的相当大的对象。 I was wondering if this is bad practice.我想知道这是否是不好的做法。 Should I create adapter classes to convert them and then just use the adapter in the controller.我应该创建适配器类来转换它们,然后在控制器中使用适配器。

Below is an example of how I convert the ViewModel object to a Model object in the controller:下面是我如何将 ViewModel 对象转换为控制器中的 Model 对象的示例:

    public async Task<IActionResult> Register(RegisterViewModel registerModel)
    {
        if (ModelState.IsValid)
        {
            ApplicationUser user = new ApplicationUser
            {
                UserName = registerModel.Email,
                Email = registerModel.Email,
                FirstName = registerModel.FirstName,
                LastName = registerModel.LastName,
                AddressLine1 = registerModel.AddressLine1,
                AddressLine2 = registerModel.AddressLine2,
                City = registerModel.City,
                PostCode = registerModel.PostCode,
                PhoneNumber = registerModel.PhoneNumber,
                NotificationPreference = Enum.GetName(typeof(NotificationPreference), NotificationPreference.None)
            };
            await userManager.CreateAsync(user, registerModel.Password);
        }
        return View(registerModel);
    }

So should I create the ApplicationUser here or should I create it in an adapter class.那么我应该在这里创建ApplicationUser还是应该在适配器类中创建它。

Short answer: It depends on number of things and certain personal preferences.简短回答:这取决于事物的数量和某些个人喜好。

Long Answer:长答案:

We need to take into account a number of factors in order to answer this question:为了回答这个问题,我们需要考虑许多因素:

  1. Whether some of the projects in our solution already use converters/adapters我们解决方案中的某些项目是否已经使用转换器/适配器
  2. How big the solution is at the moment目前解决方案有多大
  3. How big the solution will be in the future未来的解决方案有多大
  4. How many models/viewmodels there are (there will be)有多少模型/视图模型(会有)
  5. How precisely do we want to follow certain design patterns such as SOLID, separation of concerns etc我们希望遵循某些设计模式的精确程度如何,例如 SOLID、关注点分离等
  6. Our personal preferences and experience我们的个人喜好和经验

One simple option would be to add a ctor or overload the implicit casting operator for each ViewModel:一个简单的选择是为每个 ViewModel 添加一个 ctor 或重载隐式转换运算符:

public class ApplicationUser
{
    // Properties

    public ApplicationUser(RegisterModel registerModel)
    {
         UserName = registerModel.Email;
         Email = registerModel.Email;
         FirstName = registerModel.FirstName;
         // etc
    }

    //    OR:
    //    public static implicit operator ApplicationUser(RegisterModel) => 
              new ApplicationUser { UserName = registerModel.Email, ... };
}

For bigger/enterprise projects we could utilize AutoMapper or even create our own converters for each pair model -> viewmodel however I'd think twice before doing so.对于更大的/企业项目,我们可以使用AutoMapper甚至为每对模型创建我们自己的转换器 -> viewmodel 但是我在这样做之前会三思而后行。

Use AutoMapper, it's not that complicated.使用 AutoMapper,没那么复杂。

Startup.cs ConfigureServices: Startup.cs 配置服务:

    var mappingConfig = new MapperConfiguration(mc =>
    {
        mc.AddProfile(new RegisterViewModelProfile());

    });

    IMapper mapper = mappingConfig.CreateMapper();
    services.AddSingleton(mapper);

Create the RegisterViewModelProfile class.创建 RegisterViewModelProfile 类。 (I usually put these in the corresponding viewmodel class file, in your case its the RegisterViewModel.cs file. Others create one MapperProfile.cs and put all Profile classes in that file, but ofc you can create separate files for each.) (我通常将它们放在相应的视图模型类文件中,在您的情况下是 RegisterViewModel.cs 文件。其他人创建一个 MapperProfile.cs 并将所有 Profile 类放在该文件中,但您可以为每个类创建单独的文件。)

public class RegisterViewModelProfile : Profile
    {
        public RegisterViewModelProfile()
        {
            CreateMap<RegisterViewModel, ApplicationUser>()
                .ForMember(dest=>dest.UserName, opt=>opt.MapFrom(src=>src.Email))
                .ForMember(dest=>dest.NotificationPreference , opt=>opt.MapFrom(src=> Enum.GetName(typeof(NotificationPreference), NotificationPreference.None) ));
                //you dont need to map the other attributes because they have the same name and type in VM and in Model so AutoMapper does it automagically

            //you can map the other way around too if you need to the same way, and you can even do conditional mapping and/or overwrite data etc
            CreateMap<ApplicationUser, RegisterViewModel>()
                .ForMember(dest => d.Email, opt => opt.MapFrom(src => "Masked because of GDPR"));

        }
    }

In your controller inject the mapper, and do the mapping when you need to do:在您的控制器中注入映射器,并在需要时进行映射:

 public class JobsteplogsController : Controller
    {
        private readonly IMapper _mapper;

        public UserController(JobManagerContextCustom context, IMapper mapper)
        {
            _mapper = mapper;
        }
        public async Task<IActionResult> Register(RegisterViewModel registerModel)
        {
            if (ModelState.IsValid)
            {
                ApplicationUser user = _mapper.Map<ApplicationUser>(registerModel);

                await userManager.CreateAsync(user, registerModel.Password);
            }
            return View(registerModel);
        }

    }

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

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