【问题标题】:Using static automapper in C# asp.net performance practices在 C# asp.net 性能实践中使用静态自动映射器
【发布时间】:2018-05-17 15:55:03
【问题描述】:

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

我正在使用:

  • .net 框架 4.6.1
  • Microsoft.AspNet.Mvc 5.2.6
  • AutoMapper 6.2.2
  • EntityFramework 6.2.0

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

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

Global.asax.cs

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

        // Other configuration for MVC application...
    }
}

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));
    }
}

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; }
    }
}

代码摘要

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

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

问题

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

一些澄清:

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

我已经看过一个问题,但得到的结果好坏参半:

Non-static AutoMapper and ASP.NET MVC -> Where to place AutoMapper.CreateMaps?

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

【问题讨论】:

    标签: c# asp.net asp.net-mvc entity-framework automapper


    【解决方案1】:

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

    您对 AutoMapper 的使用是正确的。应用程序启动时的初始化是值得尊重的最佳实践。通过调用Mapper.Initialize,您正在初始化一个映射器,它是IMapper 接口的静态实例(可通过Mapper.Instance 静态属性访问)。当使用来自Mapper 类的静态成员时,您正在处理Mapper.Instance,它是相同AppDomain 中所有对象的相同实例。只要您使用相同的AppDomain 并且在映射配置中不做一些耗时的操作,您就不会影响性能(有人可能会将一些业务逻辑或耗时的逻辑放入AfterMapBeforeMap、@987654334 @、MapFrom 等)。

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

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

    AutoMapper 还附带一些配置,可以激活或停用并获得性能。

    显式编译

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

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

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

    Mapper.Configuration.CompileMappings();
    

    我使用它并将它与应用程序“预热”结合起来,在这种情况下,我的应用程序会自动启动,而不是等待第一个请求进行映射并编译我的配置计划。您可以查看answer 了解如何启用该功能。

    内联映射

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

    cfg.CreateMissingTypeMaps = false;
    

    2019 年 2 月 26 日编辑

    AutoMapper 创建者今天(2019 年 2 月 26 日)刚刚添加了一篇关于 AutoMapper Usage Guidelines 的博文。必读。

    【讨论】:

      【解决方案2】:

      我不知道这是否可以帮助你,但我已经将 AutoMapper 更新到 8.1.0 版本并且性能更好。我已经看到 7.0.0 版本的性能得到了纠正,但在最新版本中可能会更好。取决于您的实施,更新的影响很小。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-18
        • 2015-01-15
        • 1970-01-01
        相关资源
        最近更新 更多