在 C# 中使用静态自动映射器 asp.net 性能实践



如果这个问题已经在其他地方得到回答,我深表歉意;但我在这件事上发现了很多好坏参半的结果。

我正在使用:

  • .net 框架 4.6.1
  • Microsoft.AspNet.Mvc 5.2.6
  • 自动映射器 6.2.2
  • 实体框架 6.2.0

我对 ASP.net 和C#都很陌生,最近我成为了AutoMapper包的粉丝。我主要使用它来转换我从ApplicationDbContext获得的实体到我的DTO(数据传输对象)或ViewModel

我现在在我的应用程序中使用此设置来初始化和使用Controller中的Mapper

全球.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
Mapper.Initialize(AutoMapperConfiguration.Configure);
// Other configuration for MVC application...
}
}

自动映射程序配置.cs

public static class AutoMapperConfiguration
{
public static void Configure(IMapperConfigurationExpression config)
{
config.CreateMap<Post, Post.DetailsViewModel>().ForMember(post => post.CanEdit, cfg => cfg.ResolveUsing((src, dst, arg3, context) => context.Options.Items["UserId"]?.ToString() == src.UserId));
}
}

PostsController.cs and Post.cs

public class PostsController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Post post = db.Posts.Find(id);
if (post == null)
{
return HttpNotFound();
}
return View(Mapper.Map<Post.DetailsViewModel>(post, options => options.Items["UserId"] = User.Identity?.GetUserId()));
}
}

// Post.cs
public class Post
{
public int Id { get; set; }
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedAt { get; set; } = DateTime.Now;
public class DetailsViewModel
{
public int Id { get; set; }
public string UserId { get; set; }
public ApplicationUser User { get; set; }
public string Title { get; set; }
public string Content { get; set; }
/// <summary>
/// A value indicating whether the current logged in user can edit the model
/// </summary>
public bool CanEdit { get; set; }
public DateTime PostedAt { get; set; }
}
}

代码摘要

我正在一个静态类(AutoMapperConfiguration)中配置我的Mapper,该类包含一个Config方法,该方法从Global.asax.cs文件中调用并映射所需的类。

然后,在我的Controller中,我使用静态方法Mapper.Map将我的Post映射到它的DetailsViewModel

问题

自动映射器的这种使用(通过静态方法Mapper.Map)如何影响性能,有没有更好的方法来做到这一点?

一些澄清:

例如:如果我每秒收到 100 个关于不同控制器操作的请求,该怎么办?据我所知,每个请求都有一个单独的线程,但会访问Mapper.Map方法的相同内存(如果我是对的)。据我所知,这意味着性能将受到严重影响。

我已经看过一个问题,但从以下方面得到了不同的结果:

非静态自动映射器和 ASP.NET MVC -> 在哪里放置 AutoMapper.CreateMaps?

如果我错了,请纠正我。

AutoMapper(通过静态方法Mapper.Map)的这种使用如何影响性能,有没有更好的方法可以做到这一点?

您对自动映射器的使用是正确的。在应用程序启动时初始化是要遵循的最佳做法。通过调用Mapper.Initialize,您正在初始化一个映射器,该映射器是IMapper接口的静态实例(可通过静态属性访问Mapper.Instance)。使用类中的静态成员时Mapper您正在处理Mapper.Instance并且所有对象都处于同一AppDomain中。只要你使用相同的AppDomain并且不在你的映射配置中做一些耗时的事情(有人可能会把一些业务逻辑或耗时的逻辑放入AfterMapBeforeMapResolveUsingMapFrom等中,你就不会影响性能。

如果我每秒收到 100 个关于不同控制器操作的请求怎么办? 据我所知,每个请求都有一个单独的线程,但会访问 Mapper.Map 方法的相同内存(如果我是正确的)。 据我所知,这意味着性能将受到严重影响。

您的应用程序使用AppDomain的一个实例,每个请求将获得一个新线程,但该线程将在启动应用程序的同一AppDomain中运行,即执行Application_Start方法的线程,最后您将获得相同的Mapper.Instance实例,因此您的配置计划只会在第一个请求到达时进行编译和缓存。只有第一个请求会受到影响,而不会受到以下请求的影响。其他请求将使用缓存配置的计划。因此,只要您处于同一AppDomain并且不使用一些耗时的逻辑进入 AutoMapper 允许您执行的自定义映射替代方案,就不会对性能产生影响。

此外,AutoMapper还附带了一些可以激活或停用并获得性能的图形。

显式编译

正如我已经说过的,使用映射的第一个请求将编译您的配置计划。正如AutoMapper的文档所说:

由于表达式编译可能会占用大量资源,因此 AutoMapper 会在第一个映射上延迟编译类型映射计划。但是,此行为并不总是可取的,因此您可以告诉自动映射程序直接编译其映射。 对于几百个映射,这可能需要几秒钟。

您可以通过在映射配置后执行此操作来显式编译计划:

Mapper.Configuration.CompileMappings();

我使用它并将其与应用程序"预热"相结合,其中我的应用程序会自动启动,而不是等待第一个请求进行映射并编译我的配置计划。您可以查看此答案,了解如何启用该功能。

内联映射

在 6.2.0 中,AutoMapper 附带了一项称为内联映射的新功能,它允许您动态创建类型映射,而不是通过调用Mapper.Initialize方法配置它们。因为内联映射是动态创建的,因此它们在当时进行编译,因此您正在寻找的收益明确编译您的计划并没有多大帮助。所以我在我的项目中所做的,我停用了这个功能,这样任何人都不能不宣传地使用它。要停用它,请执行以下操作:

cfg.CreateMissingTypeMaps = false;

编辑 26/02/2019

AutoMapper创建者今天(26/02/2019)刚刚添加了一篇关于AutoMapper使用指南的博客文章。必读。

我不知道这是否可以帮助您,但是我已经将自动映射器更新到8.1.0版本,并且性能更好。我已经看到性能在 7.0.0 版上得到了纠正,但在最新版本中可能会更好。根据您的实现,更新的影响最小。

最新更新