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

Joa*_*oas 1 c# asp.net asp.net-mvc entity-framework automapper

预先,对于这个问题是否已经在其他地方得到解答,我深表歉意。但是我在这件事上发现了很多不同的结果。

我正在使用:

  • .net Framework 4.6.1
  • Microsoft.AspNet.Mvc 5.2.6
  • AutoMapper 6.2.2
  • 实体框架6.2.0

我对ASP.net和C#都是陌生的,最近我成为了AutoMapper软件包的粉丝。我主要是使用它来将我的实体转换ApplicationDbContextDTO的(数据传输对象)或ViewModel

现在,我在应用程序中使用此设置进行初始化并在Mapper中使用Controller

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        Mapper.Initialize(AutoMapperConfiguration.Configure);

        // Other configuration for MVC application...
    }
}
Run Code Online (Sandbox Code Playgroud)

AutoMapperConfiguration.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));
    }
}
Run Code Online (Sandbox Code Playgroud)

PostsController.cs和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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

代码摘要

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

然后,在我中,Controller我使用static方法Mapper.Map将其映射PostDetailsViewModel

问题

AutoMapper的这种用法(通过静态方法Mapper.Map)如何影响性能,还有更好的方法吗?

一些说明:

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

我已经看过一个问题,但是得到的结果却参差不齐:

非静态AutoMapper和ASP.NET MVC- > 在哪里放置AutoMapper.CreateMaps?

如果我在这方面有误,请纠正我。

Cod*_*und 7

AutoMapper的这种用法(通过静态方法Mapper.Map)如何影响性能,还有更好的方法吗?

您对AutoMapper的使用是正确的。在应用程序启动时进行初始化是值得尊重的最佳实践。通过调用,Mapper.Initialize您正在初始化一个映射器,它是IMapper接口的静态实例(可通过Mapper.Instancestatic属性访问)。当使用来自Mapper类的静态成员时,要处理的Mapper.Instance对象都是相同的,并且所有对象都位于同一实例中AppDomain。你会不会影响性能,只要长你到相同的AppDomain,而不是做一些时间在你映射配置消费(有人可能把一些业务逻辑或费时逻辑为AfterMapBeforeMapResolveUsingMapFrom等)。

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

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

AutoMapper还带有一些可以激活或取消激活并获得性能的配置。

显式编译

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

由于表达式编译可能会占用一些资源,因此AutoMapper会在第一个地图上延迟编译类型地图计划。但是,这种行为并不总是令人满意的,因此您可以告诉AutoMapper直接编译其映射。对于数百个映射,这可能需要几秒钟。

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

Mapper.Configuration.CompileMappings();
Run Code Online (Sandbox Code Playgroud)

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

内联映射

在6.2.0中,AutoMapper附带了一项称为内联映射的新功能,该功能使您可以即时创建类型映射,而不必通过调用Mapper.Initialize方法进行配置。因为内联映射是动态创建的,所以它们会在那时进行编译,因此您要寻求的明确编译计划的收益没有太大帮助。因此,在我的项目中,我禁用了此功能,因此任何人都无法以广告形式使用它。要停用它,请执行以下操作:

cfg.CreateMissingTypeMaps = false;
Run Code Online (Sandbox Code Playgroud)

编辑26/02/2019

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