Auto mapper : Mapping two objects Ignoring few properties dynamically - c#

For update using entity frame work , I have a DTO exposed to user with all properties but few properties are assigned with values.
Example
public class Employee
{
public int Id {get;set;}
public string Name {get;set;}
public Datetime CreatedDateTime {get;set;}
public Datetime UpdatedDateTime {get;set;}
}
public class EmployeeDTO
{
public int Id {get;set;}
public string Name {get;set;}
[JsonIgnore]
public Datetime CreatedDateTime {get;set;}
[JsonIgnore]
public Datetime UpdatedDateTime {get;set;}
}
Now The Dto is been populated
EmployeeDto e = new EmployeeDto ()
{
Id =1,
Name = "xxxxxxxxx"
}
Now we have called the method to update
public bool Update(EmployeeDto e )
{
var repo = _dbset.Employee.Get(x => x.id == e.Id)
var d = _mapper.Map<Employee,EmployeeDto>(repo,e);
_dbset.Employee.UpdateAsync(d);
}
** but here on mapping the Created Date and Update Date are set to default values which is giving me an SQL exception**
Please do help me in this case . Sorry If I am not clear

Add this line in your ModelMappingProfile class, it will ignore the properties you'll specify. You also check this link for understanding.
https://dotnettutorials.net/lesson/ignore-using-automapper-in-csharp/
CreateMap<EmployeeDTO, Employee>().ForMember(dest => dest.CreatedDateTime, opts => opts.Ignore()).ForMember(dest => dest.UpdatedDateTime, opts => opts.Ignore());

Related

Set property value with automapper to combination with 2 other

I am having employee table:
public class Employee
{
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
public int SupervisorId {get;set;}
}
SupervisorId is a foreign key from the same table (Employee) that points to other employee.
Then I have something like "EmployeeSupervisorDto"
public class EmployeeSupervisorDto
{
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
public string FullNameSupervisor {get;set;}
}
What I want to achievie is to use automapper to set FullNameSupervisor automaticly to combination of FirstName and LastName of supervisor.
Tried so far to do something like this:
cfg.CreateMap<Employee, EmployeeSupervisorDto>()
.ForMember(e => e.FullNameSupervisor, m => m.MapFrom(p => $"{p.LastName} {p.FirstName}"));
But I have no idea how to do reference to Id that points out to employee id that is supervisor of given employee.
To use the below solution, you will need to inject your data context to the auto mapper profile class (via constructor parameter), and also, in the ConfigureServices, add the DI of the automapper profile as shown in https://stackoverflow.com/a/49198279/9907597.
Create a method in the AutoMapper profile class:
public string GetEmployerFullName(int supervisorEmpId)
{
var supervisor = db.Employees.Find(supervisorEmpId);
return supervisor.FirstName + " " + supervisor.LastName;
}
Then create the mapping in the automapper profile class constructor as:
CreateMap<Employee, EmployeeSupervisorDto>()
.ForMember(e => e.FullNameSupervisor, m => m.MapFrom(p => GetEmployerFullName(p.SupervisorId)));
You can use either ValueResolver or something like this code if you want to use it once and not generally:
Mapper.CreateMap<Employee, EmployeeSupervisorDto>()
.ForMember(e => e.FullNameSupervisor, o => o.ResolveUsing(m => { return m.LastName + m.FirstName}));
May help you,
i try with linq approach :
public class Employee
{
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
public int SupervisorId {get;set;}
}
it give you the list of employe with sup FullNameSupervisor
var Employeelist = new List<Employee>() {...};
var EmployeeWithSup = from ep in Employeelist
join epsup in Employeelist on ep.SupervisorId equals epsup.Id
select new { Id = ep.Id,FirstName = ep.FirstName,LastName = ep.LastName,
SupervisorId = ep.SupervisorId,FullNameSupervisor = epsup.FirstName + " " + epsup.LastName };
If you want to do joins use automapper ,try the follow link :
AutoMapper to join records from 2 tables into single IEnumerable viewmodel

C# How to map inner property object to outer class with AutoMapper?

I have 3 classes:
public class CountryModel
{
public int Id { get; set; }
public string Title { get; set; }
}
public class CountryDTO
{
public int Id { get; set; }
public string Title { get; set; }
}
public class BaseCountryDTO
{
public CountryDTO Country {get; set};
}
I need to map CountryDTO to CountryModel, but through BaseCountryDTO class.
I know that I can do it like this:
CreateMap<BaseCountryDTO, CountryModel>()
.ForMember(model => model.Id, o => o.MapFrom(dto => dto.Country.Id))
.ForMember(model => model.Title, o => o.MapFrom(dto => dto.Country.Title));
But I want to do it clear, something like this:
// This is not working code, just my imagination :)
CreateMap<BaseCountryDTO, CountryModel>()
.ForMember(model => model, dto => dto.Country));
Because in model can be more than 2 properties. Is there way to do it?
#LucianBargaoanu Helped me with the link https://docs.automapper.org/en/latest/Flattening.html#includemembers
It solved my problem.
The solution looks like:
CreateMap<BaseCountryDTO, CountryModel>().IncludeMembers(s => s.Country);
CreateMap<CountryDTO, CountryModel>();
So the thing is we have to create a map of base class to our model with include of what we are really want to map. Then we should create a map of class which we are really need to map.
If the properties in CountryModel and CountryDTO have same names/types, then you can configure the mapping simply as -
CreateMap<CountryDTO, CountryModel>();
You can test the mapping as -
CountryDTO dto = new CountryDTO { Id = 4, Title = "Something" };
CountryModel model = Mapper.Map<CountryModel>(dto);
It will automatically map the properties from CountryDTO to CountryModel, no matter how many they are. You don't have to manually configure mapping for any property, or go through another class like BaseCountryDTO.

How to implement a Linq query by converting entities to DTOs?

There are 3 entities, let's say they are presented in this way:
**1 - entity**
class A
int id ;
int Name;
[Foreign key]
int id_B;
List C;
**2 - entity**
class B
int id ;
int Name;
List A;
**3 - entity**
class C
int id;
int Name;
[Foreign Key]
int id_A;
created an entity DTO (Everything is the same only without foreign keys)
1
class ADTO
int id ;
int Name;
List C;
2
class BDTO
int id ;
int Name;
List A;
3
class CDTO
int id;
int Name;
Now the request looks like this:
var quer = (await _context.A
.Include(b => b.B)
.Include(c => c.C)
.Where(u => u.Id == 1).ToListAsync())
.Select(a => new ADto
{
Id = a.Id,
//How to get information about entity B here by converting to DTO
C = a.C.Select(cdto => new CDTO{ Id = cdto.Id, Name = cdto.Name}).ToList(),
});
How to get information about entity B here by converting to DTO?
If you are querying "A" as your top-level entity then I believe you're just missing a navigation property to it's associated "B". (As it contains the B_Id FK)
1 - entity
public class A
{
public int id { get; set; }
public string Name { get; set; }
[ForeignKey("B")]
public int id_B { get; set; }
public virtual B B { get; set; }
public virtual ICollection<C> Cs { get; set;} = new List<C>();
}
Then when you project your Entities to DTOs using Select:
var query = (await _context.A
.Where(a => a.Id == 1)
.Select(a => new ADto
{
Id = a.Id,
B = new BDTO { Id = a.B.Id /* ... */ },
Cs = a.Cs.Select(c => new CDTO{ Id = c.Id, Name = c.Name}).ToList(),
}).Single();
Note that when using .Select you do not need to use .Include to reference related entities, that is only used to eager load related entities where you want to return an entity graph. (Such as when reading the entities to update values from DTOs) Also, be wary of using any ToList operations prior to using a Select as this will load entities into memory before applying things like filters, and negates the optimization of queries to fill just what Select needs.
});
Normally, I would suggest you implement an interface, that is provided on the constructor of the resulting object
so:
public interface IDbOjbect{
int Id {get;set;}
string Name{get;set;}
}
and then on your DTO object
public Class DtoObject {
public DtoOjbect(IDbOjbect source)
{
//Mapping done here.
}
}
Because then you can implement the interface on any persistence layer object, and the mapping will still work.
Because then the linq query is simply:
DbOjbectList.Select(x => new DtoObject(x));
provided DtoOjbect implements the interface.
your C would look like this:
public partial class C {
public int id {get;set;}
public string Name {get;set;}
}
public partial class C : IDbOjbect {
}
and your CDTO would look like:
public Class CDTO{
public int Id {get;set;}
public string Name {get;set;}
public CDTO(IDbOjbect source)
{
Id = source.Id;
Name = source.name;
}
}
Want to be able to make a DTO from B?
Implement IDbOjbect on your B
by using
public partial class B {
public int id {get;set;}
public string Name {get;set;}
}
public partial class B : IDbOjbect {
}
and now any C or B can be made into a CDTO.
Best part is, you can make a generic method for your B and C, use the "Where" keyword after you generic definition, and then use the Interface as the type, now you can make a single method that does the same thing based on what has the interface implementation, and this will also work for A, if you implement the interface on A.
Without further modification.
So now that you are asking questions, you original question doesn't, lets expand.
Lets say you have a ResumeInfo Object that only B has available.
You then use the NullPointer pattern together with interface segregation principle.
So you create an interface on your resumeInfo class
Example:
public interface IResumeInfo
{
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
}
Then on your ResumeInfo Object:
public partial class ResumeInfo
{
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
}
public partial class ResumeInfo : IResumeInfo
{
}
Then lets say you want a single DTO object:
public class DTOUserAndResume
{
public int id {get;set;}
public string Name {get;set;}
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
public DTOUserAndResume(IDbOjbect source, IResumeInfo resumeInfo)
{
Id = source.Id;
Name = source.name;
PlaceOfEmployment = resumeInfo.PlaceOfEmployment;
StartOfEmployment = resumeInfo.StartOfEmployment ;
EndOfEmployment = resumeInfo.EndOfEmployment ;
}
}
Now on B? I think you said you have resume data, but not on C?
you implement the IResumeInfo on both, but on B, you just get whatever data is there, but on C that has no data? NullOjbect Pattern.
Implement the interfacce, but make it return nothing.
So PlaceOfEmployment is always "" or Null.
Start data is always 1900-01-01 00:00:00 or whatever you want "nothing" to be on a not nullable object, and null on the end of employment.
So you simply claim that the data is the equavilant of a none-existing data set, because, it doesn't have a dataset to provide.
But you dont need to make a new DTO, you can just update the constructor on CDTO, it will also work fine. It might just get a bit confusing in regards to naming and stuff.
This should result in a call that looks like:
C = a.C.Select(cdto => new CDTO{cdto, cdto.ResumeInfo}).ToList();

EntityFreamwork full entity and lite entity

i have table users
user table :
Id, Name , firstName , password , email , address , dateofBrith
i want to create two entity for user table one lite and other full
[Table("user")]
public class LiteUser
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
}
second entity
public class fullUser : LiteUser
{
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
but not I get error about no column discriminator
is possible to do somthing like my entity are same but one have more filed then the other entity
thank you in advance for help
Unfortunately, no. You can only define one entity to one table. Instead, you'd have to do a manual .Select off of the full entity to return a custom "Lite" entry because EF needs to know all the columns that tie to a specific table from the start.
Edit: The only way around this would be to create a view and map to that instead.
You can do something like this
[Table("user")]
public class LiteUser
{
public string Name {get;set;}
public int firstName{get;set;}
}
public class fullUser : LiteUser
{
public int ID {get;set;}
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
Use primary key public int ID {get;set;} value in the derived class
As Daniel points out, a table can be associated to a single entity definition, outside of Table Per Hierarchy inheritance, which isn't what you are looking for.
This was an old trick I used with NHibernate which isn't supported in EF.
With EF you can utilize Linq and ViewModels to avoid the need of Lite vs. Full models.
Given:
//Entity
public class User
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
// View Models...
public class LiteUserViewModel
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
}
public class FullUserViewModel : LiteUserViewModel
{
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
Querying..
//Give me a list of lite data..
var viewModels = context.Users
.Where(x => x.DateOfBirth < startDate)
.Select(x => new LiteUserViewModel
{
UserId = x.UserId,
Name = x.Name,
FirstName = x.FirstName
}).ToList();
// Give me a full user.
var viewModel = context.Users
.Where(x => x.UserId = userId)
.Select(x => new FullUserViewModel
{
UserId = x.UserId,
// ... etc ...
}).SingleOrDefault();
You can leverage libraries like AutoMapper to handle mapping entity to view model. In cases where you just want to inspect data you don't need to define a view model / DTO, just use an anonymous type. The end result is the same in that EF will execute an optimized query to just return back the data you want rather than entire entities. You can optimize view models to flatten down hierarchical data using this technique. You do need to ensure that any methods or transformations in the .Select() are pure and EF compatible because EF will attempt to translate and pass those to SQL. More complex transformations should be done in the view model itself, or utilize an anonymous type select of the raw data, followed by a ToList/Single/etc. then .Select() into the view model with appropriate transformations via Linq2Object.
One option is to use table splitting which is when you map a single table to two or more entities. The difference with your requested solution is that the "additional" properties in the "full" configuration will be represented by another entity type. Example (for EF Core; EF6 will be very similar):
public class SplitTablePrincipal
{
[Key]
public int Id { get; set; }
public string PrincipalProperty { get; set; }
// principal entity has a nav property to the dependent entity
public virtual SplitTableDependent Dependent { get; set; }
}
public class SplitTableDependent
{
[Key]
public int Id { get; set; }
public string DependentProperty { get; set; }
}
public class SplitTablePricipalConfiguration : IEntityTypeConfiguration<SplitTablePrincipal>
{
public void Configure( EntityTypeBuilder<SplitTablePrincipal> builder )
{
//builder.HasKey( pe => pe.Id );
// establish 1:? relationship w/ shared primary key
builder.HasOne( pe => pe.Dependent )
.WithOne()
.HasForeignKey<SplitTableDependent>( de => de.Id ); // FK is PK
builder.ToTable( "YourTableName" );
}
}
public class SplitTableDependentConfiguration : IEntityTypeConfiguration<SplitTableDependent>
{
public void Configure( EntityTypeBuilder<SplitTableDependent> builder )
{
//builder.HasKey( de => de.Id );
// map dependent entity to same table as principal
builder.ToTable( "YourTableName" ); // same table name
}
}
You only need to include a DbSet for the SplitTablePrincipal entity type in your DbContext. When querying, the Dependent property will not be populated by default (your "lite" configuration); you would need to eager load the property for the "full" data configuration via .Include( stp => stp.Dependent ). You could also lazy load or explicitly load the Dependent property further down the line should you so choose. For example:
dbContext.Entry( principalEntity ).Reference( p => p.Dependent ).Load();

Automapper custom many-to-one conversion

Automapper Many To One conversion
How to convert values of many properties from the source object to a single type in destination object?
Can I use in this case Value Resolvers? Or maybe there is better solution?
Documentation
Here is example from documentation - one to one conversion
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.Total,
opt => opt.ResolveUsing<CustomResolver>().FromMember(src => src.SubTotal));
Mapper.CreateMap<OtherSource, OtherDest>()
.ForMember(dest => dest.OtherTotal,
opt => opt.ResolveUsing<CustomResolver>().FromMember(src => src.OtherSubTotal));
public class CustomResolver : ValueResolver<decimal, decimal> {
// logic here
}
Case
I want to transfer two objects into one (many to one conversion). For example:
public class Document
{
public int CurrencyId {get; set;}
public int ExchangeRateId {get; set;}
}
public class DocumentDto
{
public Currency Currency {get; set;}
}
public class CurrencyDetails
{
public Currency Currency {get; private set;}
public ExchangeRate ExchangeRate {get; private set;}
public CurrencyDetails(Currency currency, ExchangeRate exchangeRate)
{
Currency = currency;
ExchangeRate = exchangeRate;
}
}
I would like to achieve something like that:
public class CurrencyResolver : ValueResolver<int, int, CurrencyDetails>
{
protected override Currency ResolveCore(int currencyId, int exchangeRateId)
{
var currency = new Currency(currencyId); //simplified logic
var exchangeRate = new ExchangeRate(exchangeRateId);
var currencyDetails = new CurrencyDetails(currency, exchangeRate);
return currencyDetails;
}
}
I know that I can pass the whole object as the source object, but for me it is not a solution:
ValueResolver<Document, Currency>
I can't use full object, because I have many document types and i don't want to create new resolver for each document.
Ignoring the element (for manual conversion) is also not allowed in my case. Currency conversion logic must be conducted by AutoMapper.
It's important for me that the conversion took place in background (during the conversion of the main subject).
For example:
Document document;
var documentDto = Mapper.Map<DocumentDto>(document); // and in this moment i have proper CurrencyDetails object!
Thank you for your advice.
My solutions
I figured two solutions, but I dont like them (soooo dirty)
Solution 1 - wrap a class with interface:
public interface ICurrencyHolder
{
int CurrencyId {get; set;}
int ExchangeRateId {get; set;}
}
public class Document : ICurrencyHolder
{
public int CurrencyId {get; set;}
public int ExchangeRateId {get; set;}
}
and use resolver with following parameters:
ValueResolver<ICurrencyHolder, Currency>
Solution 2 - take as source element object type and take values via reflection
ValueResolver<object, Currency>
This is terrible!
If I understand correctly, you need to do the following mapping: from (CurrencyId, ExchangeRateId) to Currency. You can achieve it using Tuple (it is a standard .Net class very handy in these cases):
Mapper.CreateMap<Tuple<int,int>, Currency>()
.ForMember(x => x.Currency, cfg => cfg.MapFrom(y => new Currency(y.Item1, y.Item2));
Invoke the mapper as follows:
Mapper.Map<Tuple<int,int>, Currency>(Tuple.Create(doc.CurrencyId, doc.ExchangeRateId));
Maybee you can map it like this:
Mapper.CreateMap<Source, Destination>()
.ConstructUsing(s => Mapper.Map<Source, Currency>(s));
Mapper.CreateMap<Source, Currency>()
.ForMember(dst => dst.CurrencySymbol, map => map.MapFrom(src => src.DocumentDto.CurrencySymbol))
.ForMember(dst => dst.ExchangeRate , map => map.MapFrom(src => src.Document.ExchangeRate ));
Also possible:
Mapper.CreateMap<Source, Destination>()
.ConstructUsing(s => Mapper.Map<Source, Currency>(s));
Mapper.CreateMap<Source, Currency>()
.ConstructUsing(s => Mapper.Map<DocumentDto, Currency>(s))
.ConstructUsing(s => Mapper.Map<Document, Currency>(s));
Mapper.CreateMap<DocumentDto, Currency>();
Mapper.CreateMap<Document, Currency>();
If you are sure you will be doing this for every document type:
Document document;
var documentDto = Mapper.Map<DocumentDto>(document);
Then you will have to define mappings for each one of them. So I'd definitely go with the ICurrencyHolder idea and use a resolver like this:
The resolver
public class CurrencyResolver : ValueResolver<ICurrencyHolder, Currency>
{
protected override Currency ResolveCore(ICurrencyHolder source)
{
return new Currency(source.CurrencyId, source.ExchangeRateId);
}
}
Document "types"
public class Document : ICurrencyHolder
{
public int CurrencyId { get; set; }
public int ExchangeRateId { get; set; }
}
public class ExtendedDocument : ICurrencyHolder
{
public DateTime SomeDate { get; set; }
public int CurrencyId { get; set; }
public int ExchangeRateId { get; set; }
}
public interface ICurrencyHolder
{
int CurrencyId { get; set; }
int ExchangeRateId { get; set; }
}
And the mappings:
Mapper.CreateMap<Document, DocumentDto>().ForMember(m => m.Currency, opt => opt.ResolveUsing<CurrencyResolver>());
Mapper.CreateMap<ExtendedDocument, DocumentDto>().ForMember(m => m.Currency, opt => opt.ResolveUsing<CurrencyResolver>());
With that in place you can create your dto's like this and get the Currency resolve itself for you at the mapping stage:
var dto = Mapper.Map<DocumentDto>(document);
var extendedDto = Mapper.Map<DocumentDto>(extendedDocument);

Categories