# خودکارسازی فرآیند نگاشت اشیاء در AutoMapper

قرار دادن تمامی تنظیمات نگاشت‌ها درون کلاس‌‌های پروفایل تا حدودی حجم کدهای ما را در آینده زیاد خواهد کرد. public class TestProfile1 : Profile { protected override void Configure() { // این تنظیم سراسر

- Published: 2015-08-23
- Language: fa
- Tags: DNTips
- Canonical: https://sirwan.info/blog/fa/dntips-2194

---

> این نوشته نخستین بار در [دات‌نت تیپس](https://www.dntips.ir/post/2194) منتشر شده است.

<div class="postBody">قرار دادن تمامی تنظیمات نگاشت‌ها درون کلاس‌‌های پروفایل تا حدودی حجم کدهای ما را در آینده زیاد خواهد کرد.<div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">public class TestProfile1 : Profile&#10;{&#10;    protected override void Configure()&#10;    {&#10;        // این تنظیم سراسری هست و به تمام خواص زمانی اعمال می‌شود&#10;        this.CreateMap&lt;DateTime, string&gt;().ConvertUsing(new DateTimeToPersianDateTimeConverter()); &#10;        this.CreateMap&lt;User, UserViewModel&gt;();&#10;       // Other mappings&#10;     }&#10;  &#10;    public override string ProfileName&#10;    {&#10;        get { return this.GetType().Name; }&#10;    }&#10;}</pre>
 </div> </div> <div>در ادامه می‌خواهیم به روشی جهت سازماندهی بهتر این نوع کلاس‌ها بپردازیم. به طوری‌که تعاریف مربوط به نگاشت‌ها در کنار View Modelهای برنامه قرار گیرند. برای اینکار ابتدا اینترفیس‌های زیر را ایجاد خواهیم کرد:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">public interface IMapFrom&lt;T&gt;&#10;{&#10;&#10;}&#10;public interface IHaveCustomMappings&#10;{&#10;      void CreateMappings(IConfiguration configuration);&#10;}</pre>
 </div>
خوب، همانطور که مشاهده می‌کنید، در اینترفیس IMapFrom امضای هیچ متدی تعریف نشده است. در واقع View Model‌های ما از این اینترفیس جهت تشخیص اینکه به چه مدلی قرار است نگاشت شوند، استفاده خواهند کرد. اما در حالتی‌که نیاز به نگاشت صریح پراپرتی‌های یک View Model داشتیم می‌توانیم اینترفیس IHaveCustomMappings را پیاده‌سازی کرده و جزئیات نگاشت را درون متد CreateMappings تعیین کنیم.</div> <div>به عنوان مثال View Model زیر را در نظر بگیرید:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">public class PersonViewModel : IMapFrom&lt;Person&gt;&#10;{&#10;       public string Name { get; set; }&#10;       public string LastName { get; set; }&#10;}</pre>
 </div>
خوب، در اینجا با پیاده‌سازی اینترفیس IMapFrom نوع مبدا را برای ویومدل فوق مشخص کرده‌ایم. در این‌حالت هدف ما نگاشت تمامی خواص کلاس Person به تمامی خواص کلاس PersonViewModel خواهد بود. برای حالت‌های خاص نیز که نیاز به نگاشت دقیق خواص باشد به اینصورت عمل خواهیم کرد:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">public class PersonViewModel : IHaveCustomMapping&#10;{&#10;      public string Name { get; set; }&#10;      // دیگر پراپرتی‌ها&#10;     &#10;      public void CreateMappings(IConfiguration configuration)&#10;      {&#10;             configuration.CreateMap&lt;ApplicationUser, PersonViewModel&gt;()&#10;                   .ForMember(m =&gt; m.Name, opt =&gt; &#10;                         opt.MapFrom(u =&gt; u.ApplicationUser.UserName));&#10;             // دیگر نگاشت‌ها&#10;      }&#10;}</pre>
 </div>
خوب، در نهایت با استفاده از امکانات LINQ و Reflection کار پردازش تنظیمات نگاشت‌های هر View Model و خودکارسازی فرآیند نگاشت را انجام خواهیم داد. اینکار را می‌توانیم درون یک کلاس با نام AutoMapperConfig و با پیاده‌سازی اینترفیس <a href="/blog/fa/dntips-2178/">IRunInit</a> انجام دهیم:</div> <div align="left" dir="ltr" style="direction: ltr;"> <div align="left" dir="ltr" style="direction: ltr;"> <div align="left" dir="ltr" style="direction: ltr;"> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">public void Execute() &#10;{&#10;      var types = Assembly.GetExecutingAssembly().GetExportedTypes();&#10;&#10;      LoadStandardMappings(types);&#10;&#10;      LoadCustomMappings(types);&#10;}</pre>
 </div> </div> </div> </div>
در داخل متد Execute دو متد به نام‌های LoadStandardMappings و LoadCustomMapping را فراخوانی کرده‌ایم. متد اول برای پردازش حالتی است که اینترفیس IMapFrom را پیاده‌سازی کرده باشیم و متد دوم نیز برای حالتی است که اینترفیس IHaveCustomMappings را پیاده‌سازی کرده باشیم.<div> <b> <br/>
متد LoadStandardMappings</b>:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">private static void LoadStandardMappings(IEnumerable &lt;Type&gt; types) &#10;{&#10;     var maps = (from t in types&#10;                      from i in t.GetInterfaces()&#10;                      where i.IsGenericType &amp;&amp; i.GetGenericTypeDefinition() == typeof(IMapFrom&lt; &gt;)  &amp;&amp; !t.IsAbstract &amp;&amp; !t.IsInterface&#10;                      select new {&#10;                               Source = i.GetGenericArguments()[0],&#10;                               Destination = t&#10;                      }).ToArray();&#10;&#10;      foreach(var map in maps) &#10;      {&#10;               Mapper.CreateMap(map.Source, map.Destination);&#10;      }&#10;}</pre>
 </div> <b>توضیح کدهای فوق</b>:</div> <div> <ol> <li> <span style="line-height: 1.5em; font-size: 9pt;">ابتدا تمامی typeهای تعریف شده در پروژه به متد فوق پاس داده خواهند شد. </span> <br/> </li> <li> <span style="line-height: 1.5em; font-size: 9pt;">برای هر type تمامی اینترفیس‌هایی که توسط این type پیاده‌سازی شده باشند را دریافت خواهیم کرد.</span> <br/> </li> <li> <span style="line-height: 1.5em; font-size: 9pt;">سپس هر type که اینترفیس IMapFrom را پیاده‌سازی کرده باشد را پردازش می‌کنیم.</span> <br/> </li> <li> <span style="line-height: 1.5em; font-size: 9pt;">سپس از نوع‌های Abstract و Interface صرفنظر خواهیم کرد.</span> <br/> </li> <li> <span style="line-height: 1.5em; font-size: 9pt;">انواع مبدا و مقصد را برای AutoMapper فراهم خواهیم کرد.</span> <br/> </li> <li> <span style="font-size: 9pt; line-height: 1.5em;">در نهایت AutoMapper براساس آنها نگاشت را ایجاد خواهد کرد. </span> <br/> </li> </ol> </div> <div> <div> <b> <br/>
 متد LoadCustomMapping:</b> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">private static void LoadCustomMappings(IEnumerable &lt;Type&gt; types) &#10;{&#10;     var maps = (from t in types&#10;                      from i in t.GetInterfaces()&#10;                      where typeof(IHaveCustomMappings).IsAssignableFrom(t) &amp;&amp; !t.IsAbstract &amp;&amp; !t.IsInterface&#10;                      select(IHaveCustomMappings) Activator.CreateInstance(t)).ToArray();&#10;&#10;     foreach(var map in maps) &#10;     {&#10;               map.CreateMappings(Mapper.Configuration);&#10;     }&#10;}</pre>
 </div> <b> <br/>
توضیح کدهای فوق:</b> </div> <div>این متد نیز همانند متد قبلی، تمامی typeها را پردازش خواهد کرد. با این تفاوت که مواردی که <span style="line-height: 1.5em; font-size: 9pt;">اینترفیس IHaveCustomMappings را پیاده‌سازی کرده باشند، دریافت کرده و در نهایت متد CreateMappings آنها را فراخوانی خواهیم کرد.</span> </div> <div> <span style="font-size: 9pt; line-height: 1.5em;">اکنون کدهای نگاشت برنامه از اصول </span> <a href="https://www.dntips.ir/news/details/2486">Open and Closed</a> <span style="font-size: 9pt; line-height: 1.5em;"> پیروی می‌کنند. در نتیجه می‌توانیم نگاشت‌های جدید را به سادگی و با ایجاد </span>View Model<span style="font-size: 9pt; line-height: 1.5em;"> ها تعریف کنیم.</span> </div> </div></div>
