65.9K
CodeProject 正在变化。 阅读更多。
Home

在 MVC 应用程序中使用 AutoMapper 进行 CRUD 操作

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (81投票s)

2013 年 8 月 19 日

CPOL

6分钟阅读

viewsIcon

287080

downloadIcon

10316

介绍如何通过 AutoMapper 实现自定义映射和实体到实体之间的映射。

引言

在我们关于 学习 MVC 的系列文章中,我们学习了很多关于 MVC 的知识,关于在 MVC 应用程序中与数据库通信的各种技术,以及一些内部概念。

当我们投入到真实的编程环境中时,我们会面临代码中一个甚至多个挑战。本文介绍了一个新概念:MVC 应用程序中的 Auto Mapper,以克服我们在与数据实体通信和将其绑定到模型时面临的一个主要挑战。

挑战

有时,在与真实(数据库)实体交互并将其绑定到我们的模型时,我们会遇到这样的情况:

var dbContext = new MyDBDataContext();
var userDetails = dbContext.Users.FirstOrDefault(userId => userId.UserId == id);
var user = new LearningMVC.Models.User();
if (userDetails != null)
{
    user.UserId = userDetails.UserId;
    user.FirstName = userDetails.FirstName;
    user.LastName = userDetails.LastName;
    user.Address = userDetails.Address;
    user.PhoneNo = userDetails.PhoneNo;
    user.EMail = userDetails.EMail;
    user.Company = userDetails.Company;
    user.Designation = userDetails.Designation;
}
return View(user);

上面提到的代码并不难理解。在上面的代码中,从 LINQ to SQL 的 Context 类创建了一个实例 var dbContext = new MyDBDataContext();,然后从用户特定的表中获取用户详细信息并存储在 var userDetails 变量中。我们有一个现有的名为 User 的模型(LearningMVC.Models.User()),它具有与数据库生成的 Users 类相似的属性。现在,我们从数据库中的 User 类实例的属性初始化我们模型实例的属性,以便我们可以填充 MVC 应用程序中的视图。

在这里,我们看到有八个属性是相似的,但每组属性都位于不同的类中,一个在模型中,一个在 Users 类中。而我们所做的就是逐个将这些属性绑定到我们的模型,然后将其传递给视图。现在的问题是,如果数据库中有 100 列记录,而我们的模型也有相同数量的属性,并且代码需要在不同的场景中重复 6-7 次?我们是否仍然遵循这种将每个属性从数据库绑定到模型的策略?相信我,代码将有 100 页长,并且仅绑定模型从域实体中提取的数据就需要五倍的精力。

为了克服这种繁琐的情况,AutoMapper 被引入了。它不仅减少了工作量,还缩短了执行如此多行代码所需的执行时间。

Auto Mapper

AutoMapper 是一个开源库,可在 GitHub 上找到。

根据 AutoMapper CodePlex 网页:“AutoMapper 是一个对象到对象的映射器。对象到对象的映射通过将输入对象的一种类型转换为输出对象的另一种类型来工作。AutoMapper 有趣之处在于它提供了一些有趣的约定,可以免去找出如何将类型 A 映射到类型 B 的繁琐工作。只要类型 B 符合 AutoMapper 建立的约定,几乎不需要任何配置就可以映射两种类型。” 因此,它为我们的映射问题提供了解决方案。

安装 AutoMapper

首先,在你的 Visual Studio IDE 中安装 NuGet 包管理器。完成后,转到

工具 -> 程序包管理器 -> 程序包管理器控制台

然后在 Visual Studio 底部的控制台窗口中,键入

PM> Install-Package AutoMapper

按 Enter,这将安装 AutoMapper,下次在 Visual Studio 中打开 MVC 应用程序时,它将自动向项目添加 DLL 引用。

AutoMapper 实战

首先,让我们创建一个 MVC 应用程序。你可以按照我的文章创建 MVC 应用程序并使用 LINQ to SQL 将其连接到数据库: https://codeproject.org.cn/Articles/620197/Learning-MVC-Part-2-Creating-MVC-Application-and-P 

我还附上了未使用 AutoMapper 的现有 MVC 应用程序的代码。现在,让我们逐个评估所有 Controller Actions 并使用 AutoMapper 转换代码。

步骤 1:为现有应用程序创建数据库,数据库脚本已随源代码一起提供

在 Visual Studio 中打开现有的 MVC 应用程序

看到 AutoMapper 已在项目中引用,现在在 MyController 中使用该命名空间,如下所示:

步骤 2:Index Action

在我们控制器 MyController(位于Controllers文件夹下)的第一个 Action,Index Action 中,我们看到以下代码:

public ActionResult Index()
{
    var dbContext = new MyDBDataContext();
    var userList = from user in dbContext.Users select user;
    var users = new List<LearningMVC.Models.User>();
    if (userList.Any())
    {
        foreach (var user in userList)
        {
            users.Add(new LearningMVC.Models.User()
                {
                    UserId = user.UserId, 
                    Address = user.Address, 
                    Company = user.Company, 
                    FirstName = user.FirstName,
                    LastName = user.LastName,
                    Designation = user.Designation,
                    EMail = user.EMail, 
                    PhoneNo = user.PhoneNo
                });
        }
    }
  
    return View(users);
}

AutoMapper 在这里会派上用场。你知道它将用于替换代码中逐个进行的属性映射,因此,在第一行代码中,定义一个 AutoMap。要创建默认映射,请调用 Mapper.CreateMap<T1, T2>() 并指定正确的类型。在这种情况下,T1 将是 LearningMVC.UserT2 将是 LearningMVC.Models.User

Mapper.CreateMap<LearningMVC.User, LearningMVC.Models.User>();
  • LearningMVC.User -> DTO 对象类
  • LearningMVC.Models.User -> 用于绑定视图的模型类

因此,在这里,我们借助 AutoMapper 类定义了 DTO 和模型类之间的映射。

现在,在 foreach 循环中,用以下代码替换整个代码:

LearningMVC.Models.User userModel = Mapper.Map<LearningMVC.User, LearningMVC.Models.User>(user);
users.Add(userModel);

最后调用 Mapper.Map<T1, T2>(obj1) 以获取 T2 的映射对象。

因此,我们最终的 Action 代码是:

public ActionResult Index()
{
    Mapper.CreateMap<LearningMVC.User, LearningMVC.Models.User>();
    var dbContext = new MyDBDataContext();
    var userList = from user in dbContext.Users select user;
    var users = new List<LearningMVC.Models.User>();
    if (userList.Any())
    {
        foreach (var user in userList)
        {
            LearningMVC.Models.User userModel = 
              Mapper.Map<LearningMVC.User, LearningMVC.Models.User>(user);
            users.Add(userModel);
        }
    }
    return View(users);
}

我们现在看到,我们摆脱了逐个匹配属性的繁琐工作。现在运行应用程序,您将看到应用程序像以前一样运行。

步骤 3:Details Action

现有代码
public ActionResult Details(int? id)
{
    var dbContext = new MyDBDataContext();
    var userDetails = dbContext.Users.FirstOrDefault(userId => userId.UserId == id);
    var user = new LearningMVC.Models.User();
    if (userDetails != null)
    {
        user.UserId = userDetails.UserId;
        user.FirstName = userDetails.FirstName;
        user.LastName = userDetails.LastName;
        user.Address = userDetails.Address;
        user.PhoneNo = userDetails.PhoneNo;
        user.EMail = userDetails.EMail;
        user.Company = userDetails.Company;
        user.Designation = userDetails.Designation;
    }
    return View(user);
}
使用 AutoMapper 的新代码
public ActionResult Details(int? id)
{
    var dbContext = new MyDBDataContext();
    Mapper.CreateMap<LearningMVC.User, LearningMVC.Models.User>();
    var userDetails = dbContext.Users.FirstOrDefault(userId => userId.UserId == id);
    LearningMVC.Models.User user = 
      Mapper.Map<LearningMVC.User, LearningMVC.Models.User>(userDetails);
    return View(user);
}

步骤 4:Create Action (POST)

现有代码
[HttpPost]
public ActionResult Create(LearningMVC.Models.User userDetails)
{
    try
    {
        var dbContext = new MyDBDataContext();
        var user = new User();
        if (userDetails != null)
        {
            user.UserId = userDetails.UserId;
            user.FirstName = userDetails.FirstName;
            user.LastName = userDetails.LastName;
            user.Address = userDetails.Address;
            user.PhoneNo = userDetails.PhoneNo;
            user.EMail = userDetails.EMail;
            user.Company = userDetails.Company;
            user.Designation = userDetails.Designation;
        }
        dbContext.Users.InsertOnSubmit(user);
        dbContext.SubmitChanges();
        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}
使用 AutoMapper 的新代码
[HttpPost]
public ActionResult Create(LearningMVC.Models.User userDetails)
{
    try
    {
        Mapper.CreateMap<LearningMVC.Models.User, LearningMVC.User>();
        var dbContext = new MyDBDataContext();
        var user = Mapper.Map<LearningMVC.Models.User, LearningMVC.User>(userDetails);
        dbContext.Users.InsertOnSubmit(user);
        dbContext.SubmitChanges();
        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

请注意,在这里我们交换了映射,因为现在对于 Create Action,我们需要从 Model 读取并绑定到我们的 DTO,所以只需交换映射并运行应用程序。现在我们的 T1 是 Model,T2 是 DTO。

步骤 5:Edit Action

现有代码
public ActionResult Edit(int? id)
{
    var dbContext = new MyDBDataContext();
    var userDetails = dbContext.Users.FirstOrDefault(userId => userId.UserId == id);
    var user = new LearningMVC.Models.User();
    if (userDetails != null)
    {
        user.UserId = userDetails.UserId;
        user.FirstName = userDetails.FirstName;
        user.LastName = userDetails.LastName;
        user.Address = userDetails.Address;
        user.PhoneNo = userDetails.PhoneNo;
        user.EMail = userDetails.EMail;
        user.Company = userDetails.Company;
        user.Designation = userDetails.Designation;
    }
    return View(user);
}
使用 AutoMapper 的新代码
public ActionResult Edit(int? id)
{
    Mapper.CreateMap<LearningMVC.User, LearningMVC.Models.User>();
    var dbContext = new MyDBDataContext();
    var userDetails = dbContext.Users.FirstOrDefault(userId => userId.UserId == id);
    var user = Mapper.Map<LearningMVC.User, LearningMVC.Models.User>(userDetails)
    return View(user);
}

步骤 6:Delete Action

现有代码
public ActionResult Delete(int? id)
{
    var dbContext = new MyDBDataContext();
    var user = new LearningMVC.Models.User();
    var userDetails = dbContext.Users.FirstOrDefault(userId => userId.UserId == id);
    if (userDetails != null)
    {
        user.FirstName = userDetails.FirstName;
        user.LastName = userDetails.LastName;
        user.Address = userDetails.Address;
        user.PhoneNo = userDetails.PhoneNo;
        user.EMail = userDetails.EMail;
        user.Company = userDetails.Company;
        user.Designation = userDetails.Designation;
    }
    return View(user);
}
使用 AutoMapper 的新代码
public ActionResult Delete(int? id)
{
    var dbContext = new MyDBDataContext();
    Mapper.CreateMap<LearningMVC.User, LearningMVC.Models.User>();
    var userDetails = dbContext.Users.FirstOrDefault(userId => userId.UserId == id);
    var user = Mapper.Map<LearningMVC.User, LearningMVC.Models.User>(userDetails);
    return View(user);
}

ForMember() 和 MapFrom() 在 AutoMapper 中

AutoMapper 中有两个重要函数在对象映射中起着重要作用。假设我们的 model/viewmodel 类有一个属性 FullName,并且我们希望从 DTO 中添加用户的名字和姓氏来构成完整的名字并将其绑定到模型。对于这些情况,ForMember()MapFrom() 非常有用。请参见下面的代码:

Mapper.CreateMap<LearningMVC.User, LearningMVC.Models.User>().ForMember(emp => emp.Fullname,
map => map.MapFrom(p => p.FirstName + " " + p.LastName));

在这里,我们说模型类中的 FullName 属性使用 User DTO 中的 FirstNameLastName 属性进行映射。

代码本身不言自明。这种映射也称为自定义映射。

结论

在本篇文章中,我们学习了如何通过 AutoMapper 实现自定义映射和实体到实体之间的映射。由于这只是对该概念的一个概览,因此还有很多内容可以深入探讨。

我跳过了 Edit 和 Delete 的 POST 方法,这将是你的一个家庭作业。一旦你完全理解并掌握了,你就可以轻松地完成那两个待定的 Action 了。

本文于 2013 年 10 月 29 日被选为 Microsoft 网站 http://www.asp.net/community/articles 上的 今日文章 。 [^]

 

祝您编码愉快!

© . All rights reserved.