.Net Core projemizde dilersek AutoMapper, dilersek Mapster kullanabileceğimiz şekilde mapper konusunu anlattım.

Uygulamalarımızda data katmanımızda bulunan entity nesneleri ile UI kısmı direkt olarak iletişimde olmasın bunun yerine araya bir dto(data transfer object) nesnesi belirleyip iletişimi böyle kurarız.

Bu bir çok geliştiricinin uyguladığı bir kuraldır. Peki biz bu entity nesnesini, dto nesnesine set ederken kolay bir şekilde nasıl mapleyebiliriz. Tüm propertyleri tek tek el ile maplemek yerine bu işlemleri kolaylaştırmak adına yapılmış kütüphaneler var.

Biz bügün bunlardan AutoMapper ve Mapster’a değineceğiz. Neden sadece AutoMapper ya da Mapster anlatmak istemedim. Bu makalede aynı zamanda projemizde AutoMapper kullanırken, karar değiştirip Mapster kullanmak isteyebiliriz. Ya da tam tersi ya da bambaşka bir kütüphane.

.Net Core bize Dependency Injection yönetimi sağlıyor, bizde projemizi bir mappere bağımlı bırakmadan dilediğimizde istediğimizi kullanmayı sağlayalım. Öncelikle bir mapper işleminde bizim işimizi görecek bir infrastructure yapısına ihtiyacımız var. Bu yapıda bizim belirlediğimiz bir interface class ve mapper görevi görecek kütüphaneden zorunlu kıldığımız methodu impletemente etmesini isteyeceğiz.

namespace Mapper.Core
{
    public interface IMapper 
    {
        TDestination Map<TSource, TDestination>(TSource source);
    }
}

Mapper.Core adında bir class library açtım içerisinde IMapper adında bir interface oluşturup Map adında bir method ekledim.

TDestination : Bizim için ulaşmak istediğimiz nesne

TSource : Kaynak olarak göstereceğimiz nesne

Örn : Bir entity nesnesini dto nesnesine maplemek istiyorum, bu durumda “TDestination” bir “dto” nesnesi, “TSource” ise bir “entity” nesnesi olmalıdır.

Arayüzümüzü oluşturduk, şimdi bize mapper görevi görecek kütüphanelerin “IMapper” dan türemesini ve “Map” methodunu implemente etmesini istiyeceğiz.

Mapperlara geçmeden önce bir entity, birde dto nesnesi oluşturuyorum.

namespace Sample.Entity
{
    public class Customer
    {
        public string Name { get; set; }
        public string Surname { get; set; }
    }
}namespace Sample.Dto
{
    public class CustomerDto
    {
        public string Name { get; set; }
        public string Surname { get; set; }
    }
}

Property isimleri aynı olan entity ve dto nesnelerini oluşturdum. Şimdi mapperlarımıza geçelim.

AutoMapper

Nuget üzerinden projemize ekliyoruz.

AutoMapper’de ilk olarak profile tanımlaması yapmamız gerekiyor.

namespace Mapper.MyAutoMapper.Profiles
{
    public class CustomerProfile : Profile
    {
        public CustomerProfile()
        {
            CreateMap<CustomerDto, Customer>();
            CreateMap<Customer, CustomerDto>();
        }
    }
}

AutoMapper’ın “Profile” nesnesinden miras alıyoruz. CreateMap ile ben entityden dto alırım, dtodan entity alırım gibi tanımlamaları yapıyoruz.

Ayrıca burada AfterMap, BeforeMap gibi methodlara ulaşma şansım oluyor. Dto’dan entity’ye çevirirken şu işlemi yap gibi custom hale getirebiliyorum. Bu profile tanımlamalarını her entity, dto nesnesi için yapmamız gerekiyor. Daha sonra bu yaptığımız profilleri initialize etmemiz gerekli.

Bunu initialize işlemini AutoMapper’ın map görevi görecek nesnesinde yapacağım. Bu Mapper nesnesi singleton olacağı için nesne oluşturulurken bir seferliğine bu initialize işlemini yapmış olacağız.

namespace Mapper.MyAutoMapper
{
    public class Mapper : Core.IMapper
    {
        public Mapper()
        {
            var profiles = new List<Profile>
            {
                new CustomerProfile()
            };
            AutoMapper.Mapper.Initialize(config =>
            {
                foreach (var item in profiles)
                {
                    config.AddProfile(item);
                }
            });        }
        public TDestination Map<TSource, TDestination>(TSource source)
        {
            return AutoMapper.Mapper.Map<TDestination>(source);
        }
    }
}

Constructor’da profiles adında bir liste oluştururken profillerimi tanıttım. Şuan tek bir profile var, birden fazla olması durumunda bu listeye ekleyebiliriz.

Ve “IMapper”dan implemente ettiğimiz Map methodunu AutoMapper’ın Map methodunu kullanarak uyguluyorum. Şuan AutoMapper kullanıma hazır durumda.

Mapster

AutoMapper’da da olduğu gibi nuget üzerinden Mapster’ı yüklüyorum. AutoMapper’da profile tanımlaması yapmazsak eğer hata alıyoruz. Ancak Mapster için bu geçerli değil, profile tanımlaması yapma zorunlu değil. Böylece direkt olarak IMapper’ı implemente etme işlemine geçebiliyorum.

namespace Mapper.MyMapster
{
    public class Mapper : IMapper
    {
        public TDestination Map<TSource, TDestination>(TSource source)
        {
            return source.Adapt<TDestination>();
        }
    }
}

AutoMapper’dan farklı bir kullanımı var. Source olarak belirttiğimiz nesneye extension yazarak mapleme yapıyor.

Biz dışarıdan “Map” methodunu kullanacağımız için IMapper’den türeyecek sınıfların içerisinde methodu nasıl implemente ettiği ile ilgilenmiyoruz. AutoMapper ve Mapster class library olarak hazır. Her hangi bir etkileşimde olmadıkları için aksiyon almıyolar, şimdi onları .net core ile kullanalım.

Boş bir .net core mvc projesi açıyorum, bildiğiniz gibi .net core bize dependency injection yönetimi sağlıyor. Castle Windsor makalesinde buna değinmiştik. Startup.cs’de ben IMapper olarak AutoMapper ya da Mapster kullanacağımı belirtmek istiyorum.

namespace Mapper.TestUI
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }        public IConfiguration Configuration { get; }        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);            services.AddSingleton<IMapper, MyAutoMapper.Mapper>();
        }        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

Şuan MyAutoMapper.Mapper ekli, yani projemde AutoMapper kullanıyorum. AutoMapper yerine daha performansı yüksek olarak tespit edilen Mapster’a geçiş yapmak istiyorum.

services.AddSingleton<IMapper, MyAutoMapper.Mapper>();

Burada MyAutoMapper.Mapper olarak belirttiğim kodu alttaki kod ile değiştiriyorum.

services.AddSingleton<IMapper, MyMapster.Mapper>();

Böylece tek bir satır değiştirerek dilersem projemde AutoMapper, dilersem Mapster kullanabiliyorum. Yarın bir gün başka bir mapper kütüphanesi çıktı diyelim “IMapper”dan implemente ediyorum, projeme referans edip buradan tek satır değiştiriyorum.

namespace Mapper.TestUI.Controllers
{
    public class HomeController : Controller
    {
        IMapper _mapper;
        public HomeController(IMapper mapper)
        {
            _mapper = mapper;
        }
        public IActionResult Index()
        {
            
            Customer customer = new Customer
            {
                Name = "Samet",
                Surname = "Çınar"
            };
            CustomerDto customerDto = _mapper.Map<Customer, CustomerDto>(customer);
            return View();
        }
    }
}

Controller tarafında kullandığım kod yapısını hiç bir şekilde değiştirmiyorum.

Böylece .net core’un sağladığı IoC yapısını kullanarak dependency injection yönetimi yapıyorum. Kodları gösterirken namespaceler ile kopyaladım. Yine de dileyen olursa projeyi de atabilirim.

Leave a Reply

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir