How can I Automap a missmatch between two models with a different number of fields? - automapper

I use Visual Studio 2019 and .net core 3.1. I have a couple of related models, CustomerCountries and CustomerRegions in a one-to-many relationship, i.e. 1 CustomerCountries has many CustomersRegions.
In the views of the regions I want to show the name of the countries, not the id, that's why I created a ViewModel:
public class CustomerCountryRegionVM
{
public int IdCustomerRegion { get; set; }
public string CustomerRegion { get; set; }
public int IdCustomerCountry { get; set; }
public string CustomerCountry { get; set; }
}
The issue is that the regions model does not have the CustomerCountry field, it has the foreign primary key IdCustomerCountry. So I don't know how to do the mapping, I've tried it but I get a missmatch. One has 4 fields and the other 3. Do I have to make a discount for the viewmodel? or is there another option?
public class CustomerRegionsDto
{
public int IdCustomerRegion { get; set; }
public string CustomerRegion { get; set; }
[ForeignKey("CustomerCountryId")]
public int IdCustomerCountry { get; set; }
public CustomerCountriesDto CustomerCountryDto { get; set; }
}
public class CustomerRegions
{
[Key]
public int IdCustomerRegion { get; set; }
[StringLength(50, ErrorMessage = "Longitud máxima para la región: 50")]
public string CustomerRegion { get; set; }
[ForeignKey("IdCustomerCountry")]
public int IdCustomerCountry { get; set; }
public CustomerCountries CustomerCountry { get; set; }
}
Automapping class
CreateMap<CustomerRegionsDto, CustomerRegions>();
CreateMap<CustomerRegions, CustomerRegionsDto>();
CreateMap<CustomerCountryRegionVM, CustomerRegions>();
CreateMap<CustomerRegions, CustomerCountryRegionVM>();
CreateMap<CustomerCountryRegionVM, CustomerCountryRegionDto>();
CreateMap<CustomerCountryRegionDto, CustomerCountryRegionVM>();
In the controller:
var model = _mapper.Map<CustomerCountryRegionVM, CustomerRegionsDto>(ccr);
The other model:
public class CustomerCountries
{
public int IdCustomerCountry { get; set; }
[StringLength(50, ErrorMessage = "Longitud máxima para el país: 50")]
public string CustomerCountry { get; set; }
public ICollection<CustomerRegions> CustomerRegions { get; set; }
}

You can map particular property
CreateMap<BusinessService.Model.PermitDashboard, BusinessService.Model.Permit>()
.ForMember(x => x.PermitId, opt => opt.MapFrom(z => z.PermitId))
.ForMember(x => x.PermitTitle, opt => opt.MapFrom(z => z.PermitTitle))
.ForPath(x => x.PermitType.PermitTypeName, opt => opt.MapFrom(z => z.PermitType))
.ForMember(x => x.EffectiveDt, opt => opt.MapFrom(z => z.EffectiveDt))
.ForMember(x => x.ExpirationDt, opt => opt.MapFrom(z => z.ExpirationDt))
.ReverseMap();

Related

Automapper flatten from three levels to two levels

I use automapper v. 9 and I need to transform a 3 levels structure to a 2 levels structure.
I already tried with the IncludeMembers option, but I can't achieve the result I want.
Here is my model classes (simplified)
public partial class Mail
{
public Mail()
{
Mail_Files = new HashSet<Mail_Files>();
}
[Key]
public int Id { get; set; }
public string Oggetto { get; set; }
public string Testo { get; set; }
[InverseProperty("IdMailNavigation")]
public virtual ICollection<Mail_Files> Mail_Files { get; set; }
}
public partial class Mail_Files
{
[Key]
public int Id { get; set; }
public int IdMail { get; set; }
public int IdFile { get; set; }
[ForeignKey(nameof(IdFile))]
[InverseProperty(nameof(Files.Mail_Files))]
public virtual Files IdFileNavigation { get; set; }
[ForeignKey(nameof(IdMail))]
[InverseProperty(nameof(Mail.Mail_Files))]
public virtual Mail IdMailNavigation { get; set; }
}
public partial class Files
{
public Files()
{
Mail_Files = new HashSet<Mail_Files>();
}
[Key]
public int Id { get; set; }
public int IdTipoFile { get; set; }
public string FileName { get; set; }
public int? FileSize { get; set; }
[InverseProperty("IdFileNavigation")]
public virtual ICollection<Mail_Files> Mail_Files { get; set; }
}
I have to flatten this object to this:
public class MailFileResource
{
public int Id { get; set; }
public string FileName { get; set; }
}
public class MailResource
{
public int Id { get; set; }
public string Testo { get; set; }
public IEnumerable<MailFileResource> Files { get; set; }
}
I do it this way:
CreateMap<Files, MailFileResource>()
.ForMember(d => d.FileName, opt => opt.MapFrom(s => s.FileName))
.ForMember(d => d.Id, opt => opt.MapFrom(s => s.Id));
CreateMap<Mail_Files, MailFileResource>().IncludeMembers(s => s.IdFileNavigation);
CreateMap<Mail, MailResource>()
.ForMember(d => d.Files , opt => opt.MapFrom(s => s.Mail_Files))
.ForMember(d => d.Protezione, opt => opt.MapFrom(s => s.Protezione));
My mistake was to use IncludeMembers in Mail -> MailResource mapping too

How to use same complex type as a property of several output types but different source types

I have a class for CountryAndCity:
public class CountryAndCity
{
public Country Country { get; set; }
public City City { get; set; }
public ZipCode ZipCode { get; set; }
}
I am using this class in several output classes, for example:
public class OutputClassA
{
public CountryAndCity CountryAndCity { get; set; }
}
public class OutputClassB
{
public CountryAndCity CountryAndCity { get; set; }
}
In addition, i have some "Input Classes" which includes the same relevant fields for mapping with additional other fields:
public class InputClassA : ICountryAndCity
{
...Some other properties.....
public int? CountryId { get; set; }
public string CountryDesc { get; set; }
public int? CityId { get; set; }
public string CityDesc { get; set; }
...Some other properties.....
}
public class InputClassB : ICountryAndCity
{
...Some other properties.....
public int? CountryId { get; set; }
public string CountryDesc { get; set; }
public int? CityId { get; set; }
public string CityDesc { get; set; }
...Some other properties.....
}
i didn't want to duplicate the code for every CreateMap statement for CountryAndCity property mapping for every Input and output combination types so i decided to use an interface that all the "input types" implements.
i created this configuration and its working great if i implement ICountryAndCity on all the relevant "input classes" and using "Mapper.Map" for the interface inside the CreateMap function.
Mapper.Initialize(cfg =>
{
cfg.CreateMap<InputClassA, OutputClassA>()
.ForMember(dest => dest.CountryAndCity,
opts => opts.MapFrom(src => Mapper.Map<ICountryAndCity, CountryAndCity>((ICountryAndCity)src)));
cfg.CreateMap<InputClassB, OutputClassB>()
.ForMember(dest => dest.CountryAndCity,
opts => opts.MapFrom(src => Mapper.Map<ICountryAndCity, CountryAndCity>((ICountryAndCity)src)));
cfg.CreateMap<ICountryAndCity, CountryAndCity>()
.ForMember(dest => dest.Country,
opts => opts.MapFrom(
src => new Country
{
CountryId = src.CountryId,
CountryDesc = src.CountryDesc
}))
.ForMember(dest => dest.City,
opts => opts.MapFrom(
src => new City
{
CityId = src.CityId,
CityDesc = src.CityDesc
}))
.ForMember(dest => dest.ZipCode,
opts => opts.MapFrom(
src => new ZipCode
{
ZipCodeId = src.ZipCodeId,
ZipCodeDesc = src.ZipCodeDesc
}));
}
I am sure that there is other better way to do it using AutoMapper without using interface. Can someone help with that?

How to map related data with AutoMapper

I'm trying to map three entity models into one viewmodel using AutoMapper. The final output should be a recursive category tree with products in the categories. The category tree is working, but the Products-property of the viewmodel is null. My query is returning categories and products, so I'm thinking that the mapping doesn't know how to map the products to the viewmodel.
My entity models:
public class ProductCategory
{
public int Id { get; set; }
public int SortOrder { get; set; }
public string Title { get; set; }
[ForeignKey(nameof(ParentCategory))]
public int? ParentId { get; set; }
// Nav.props:
public ProductCategory ParentCategory { get; set; }
public ICollection<ProductCategory> Children { get; set; }
public List<ProductInCategory> ProductInCategory { get; set; }
}
public class ProductInCategory
{
public int Id { get; set; }
public int ProductId { get; set; }
public int SortOrder { get; set; }
public int ProductCategoryId { get; set; }
public bool IsProductCategoryFrontPage { get; set; }
// Nav.props.
public Product Product { get; set; }
public ProductCategory ProductCategory { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
// Nav.prop:
public List<ProductInCategory> InCategories { get; set; }
}
My viewmodel:
public class ViewModelProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public string ProductCountInfo
{
get
{
return Products != null && Products.Any() ? Products.Count().ToString() : "0";
}
}
public ViewModelProductCategory ParentCategory { get; set; }
public IEnumerable<ViewModelProductCategory> Children { get; set; }
public IEnumerable<ViewModelProduct> Products { get; set; }
}
I have tried this mapping (maps categories, but no products):
CreateMap<ProductCategory, ViewModelProductCategory>();
CreateMap<ViewModelProductCategory, ProductCategory>();
I have tried this mapping (maps categories, but no products):
CreateMap<ProductCategory, ViewModelProductCategory>()
.ForMember(dto => dto.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dto => dto.ParentId, opt => opt.MapFrom(src => src.ParentId))
.ForMember(dto => dto.Title, opt => opt.MapFrom(src => src.Title))
.ForMember(dto => dto.SortOrder, opt => opt.MapFrom(src => src.SortOrder))
.ForMember(dto => dto.Children, opt => opt.MapFrom(src => src.Children))
.ForMember(dto => dto.Products, opt => opt.MapFrom(src => src.ProductInCategory));
Looks like you want to skip the junction (link) entity in the view model.
To do that, first create a mapping from Product to ViewModelProduct and then define a mapping from ProductCategory.ProductInCategory to ViewModelProductCategory.Products using projection converting List<ProductInCategory> to IEnumerable<Product>. AutoMapper will take care converting IEnumerable<Product> to List<ViewModelProduct> the same way as if you were querying Products:
CreateMap<Product, ViewModelProduct>();
CreateMap<ProductCategory, ViewModelProductCategory>()
.ForMember(dst => dst.Products, opt => opt.MapFrom(
src => src.ProductInCategory.Select(pc => pc.Product)));

Automapper How to devide properties from one object to properties of two objects same type

I have one object AllDriversDetails that contains data for two driver: First and Second.
public class AllDriversDetails
{
public string FirstDriverId { get; set; }
public string FirstDriverName { get; set; }
public string SecondDriverId { get; set; }
public string SecondDriverName { get; set; }
}
I have to convert AllDriversDetails to ComplexDriversDetails that has two properties of same type
public class ComplexDriversDetails
{
public DriverDetails FirstDriver { get; set; }
public DriverDetails SecondDriver { get; set; }
}
public class DriverDetails
{
public string Id { get; set; }
public string Name { get; set; }
}
Is it possible to do with automapper?
I should be no problem. Try something like this
Mapper.CreateMap<AllDriversDetails, ComplexDriversDetails>()
.ForMember(m => m.FirstDriver, opt => opt.MapFrom(src => new DriverDetails {Id = src.FirstDriverId, Name = FirstDriverName }))
.ForMember(m => m.SecondDriver , opt => opt.MapFrom(src => new DriverDetails {Id = src.SecondDriverId, Name = SecondDriverName }))
You can find more information here: http://cpratt.co/using-automapper-creating-mappings/

Entity Framework 4.0 CTP5 Many to Many relation ship with a help table?

i have a question about EF4 again :) sorry i can't find any thing on the net.
The question is i am using CTP5 Code Only to map (many to many), how do i do it???
But i am doing the old fashion way, i mean i have three tables:
Users
Companies.
UsersToCompanies -> (UserId, CompanyId)
How do i map this, it will be really appreciated if you would show me the code example to it,
from POCO's to Mapping.
This is the error i receive...
This is my entities
public class User
{
public int UserId { get; set; }
public int? UserRoleId { get; set; }
public string UserName { get; set; }
public string UserPassword { get; set; }
public DateTime InsertDate { get; set; }
public virtual UserRole UserRole { get; set; }
//this is my many to many prop
public virtual ICollection<Company> Companies { get; set; }
}
public class Company
{
public int CompanyId { get; set; }
public string CompanyNameHe { get; set; }
public string CompanyNameEn { get; set; }
public string CompanyParent { get; set; }
public DateTime InsertDate { get; set; }
//this is my many to many prop
public virtual ICollection<User> Users { get; set; }
}
/*relationship map*/
public class UsersCompanies
{
public int Id { get; set; }
public int UserId { get; set; }
public int CompanyId { get; set; }
}
//mapping
public class CompanyMap : BaseConfig<Company>
{
public CompanyMap()
{
/*Identity*/
HasKey(c => c.CompanyId);
Property(c => c.CompanyId).HasDatabaseGenerationOption(DatabaseGenerationOption.Identity).HasColumnName("COMPANY_ID");
/*Have default values*/
Property(c => c.InsertDate).HasDatabaseGenerationOption(DatabaseGenerationOption.Computed).HasColumnName("INSERT_DATE");
/*simple scalars*/
Property(c => c.CompanyNameHe).HasMaxLength(32).IsRequired().HasColumnName("COMPANY_NAME_HE");
Property(c => c.CompanyNameEn).HasMaxLength(32).IsRequired().HasColumnName("COMPANY_NAME_EN");
Property(c => c.CompanyParent).HasMaxLength(32).IsRequired().HasColumnName("COMPANY_PARENT");
ToTable("CMS_COMPANY", "GMATEST");
}
}
public class UserMap : BaseConfig<User>
{
public UserMap()
{
/*Identity*/
HasKey(c => c.UserId);
Property(c => c.UserId).HasDatabaseGenerationOption(DatabaseGenerationOption.Identity).HasColumnName("USER_ID");
/*Have default values*/
Property(c => c.InsertDate).HasDatabaseGenerationOption(DatabaseGenerationOption.Computed).HasColumnName("INSERT_DATE");
/*simple scalars*/
Property(c => c.UserName).HasMaxLength(25).IsRequired().HasColumnName("USER_NAME");
Property(c => c.UserPassword).HasMaxLength(25).IsRequired().HasColumnName("USER_PASSWORD");
Property(c => c.UserRoleId).IsRequired().HasColumnName("USER_ROLE_ID");
/*relationship*/
HasRequired(u => u.UserRole).WithMany().HasForeignKey(t => t.UserRoleId);
HasMany(p => p.Companies).WithMany(c => c.Users).Map(mc =>
{
mc.ToTable("UsersCompanies");
mc.MapLeftKey(p => p.UserId, "CompanyId");
mc.MapRightKey(c => c.CompanyId, "UserId");
});
ToTable("CMS_USERS", "GMATEST");
}
}
public class UsersCompaniesMap : BaseConfig<UsersCompanies>
{
public UsersCompaniesMap()
{
/*Identity*/
HasKey(k => k.Id);
Property(c => c.Id).HasDatabaseGenerationOption(DatabaseGenerationOption.Identity).HasColumnName("ID");
Property(c => c.UserId).IsRequired().HasColumnName("USER_ID");
Property(c => c.CompanyId).IsRequired().HasColumnName("COMPANY_ID");
ToTable("CMS_USERS_TO_COMPANIES", "GMATEST");
}
}
Here's the error I get:
'((new
System.Linq.SystemCore_EnumerableDebugView(ctx.FindAll())).Items[0]).Companies'
threw an exception of type
'System.Data.EntityCommandExecutionException'
Inner exception: ORA-00942: table or
view does not exist
You do not really need 3 tables to perform the mapping. You could simply do the following:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Company> Companies { get; set; }
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
And this should do it. As long as you have a virtual ICollection referencing the other entity in both POCO's, CTP5's conventions should take care of everything for you. On the other hand, if you prefer doing it manually using the Fluent API, then check this blog by the ADO.NET team (scroll a little down to the many-to-many relationship section)
The answer for my problem, i have to say many thanks to you all, but Andrey from Devart,
have given me the solution, it's simple.
First off all i have to have, three tables Companies, Users, UsersCompanies/
Second off all i have to map the tables to original names of tables example:
my users table called in Db like that: CMS_USERS i have to map it with original name.
this is the example that i use and it's really works.
HasMany(c => c.Companies)
.WithMany(u => u.Users)
.Map(mc =>
{
mc.ToTable("CMS_USERS_TO_COMPANIES", "GMATEST");
mc.MapLeftKey(c => c.UserId, "USER_ID");
mc.MapRightKey(u => u.CompanyId, "COMPANY_ID");
});
Thank you all.

Resources