Mapping one model to another - c#

I have two models, one of which I process and return it to a view and then from the view I send it to a controller. In the controller, I need to send it to a stored procedure but the stored procedure expects a model with different property names.
Here is my model:
public class Operator
{
public int OPERATOR_OBJECTID { get; set; }
public string SETTLEMENT_OBJECTID { get; set; }
public string TECHNOLOGY_OBJECTID { get; set; }
}
and here is the model the stored procedure expects
public class UploadModel
{
public int OPERATOR_OBJECTID { get; set; }
public string SETTLEMENT_CODE { get; set; }
public string TECHNOLOGY_CODE { get; set; }
}
Since I send the properties from Operator, like SETTLEMENT_OBJECTID but it expects SETTLEMENT_CODE it throws an exception. Can I somehow map the properties from one model to another or can I cast one model to another? What would be a good solution here?

As mentioned in the comments, you can use the automapper library and configure as such:
var mapConfig = new MapperConfiguration(
cfg => cfg.CreateMap<Operator, UploadModel>()
.ForMember(dest => dest.SETTLEMENT_CODE, opt => opt.MapFrom(src => src.SETTLEMENT_OBJECTID))
.ForMember(dest => dest.TECHNOLOGY_CODE, opt => opt.MapFrom(src => src.TECHNOLOGY_OBJECTID))
);
Check here the getting started guide: https://docs.automapper.org/en/stable/Getting-started.html
You can also define an explicit operator to be able to cast from one class to the other: https://www.dotnetperls.com/explicit

Related

Map two classes in runtime using column mapping information provided in a JSON file

I have two Classes like this.
public class InputModel
{
public int studentid { get; set; }
public string studentname { get; set; }
public string studentcity { get; set; }
}
public class OutputModel
{
public int StudentIDColumn { get; set; }
public string StudentNameColumn { get; set; }
public string StudentCityColumn { get; set; }
}
Now the requirement is like this:
I will receive an object of InputModel Class. From this, I need to create an object of OutputModel class.
It's simple if we use a library like AutoMapper. But the problem is, the Column-To-Column Mapping information will be supplied via a Json File like this:
{
"studentid": "StudentIDColumn",
"studentname": "StudentNameColumn",
"studentcity": "StudentCityColumn"
}
Based on the JSON mapping data, I need to Map the columns in runtime and generate the Output class object.
I tried to map the two classes using Automapper. But I am not sure how to do it in runtime using the JSON file.
var MapperConfig = new MapperConfiguration(c =>
c.CreateMap<InputCSVModel, OutputIDMModel>()
.ForMember(dest => dest.StudentIDColumn, act => act.MapFrom(src => src.studentid))
.ForMember(dest => dest.StudentNameColumn, act => act.MapFrom(src => src.studentname))
.ForMember(dest => dest.StudentCityColumn, act => act.MapFrom(src => src.studentcity))
);
var mapper = new Mapper(MapperConfig);
OutputIDMModel outModel = mapper.Map<OutputIDMModel>(inputModel);
I know that it's might be possible to do this with Reflection. But is there any better approach ?
I was able to read the JSOn file and pass the strings in the automapper config like this.
var MapperConfig = new MapperConfiguration(c =>
c.CreateMap<InputModel, OutputModel>()
.ForMember("StudentIDColumn", opt => opt.MapFrom("studentid"))
.ForMember("StudentNameColumn", opt => opt.MapFrom("studentname"))
.ForMember("StudentCityColumn", opt => opt.MapFrom("studentcity"))
);

Automapper: How to flatten complex object to plain object

public class Complex
{
public A A { get; set; }
public A B { get; set; }
}
public class A
{
public int a1 { get; set; }
public int a2 { get; set; }
}
public class B
{
public int b1 { get; set; }
public int b2 { get; set; }
}
//----------------Source Object End Here---------------------
public class Simple <----[This Simple class has only properties of A class]
{
public int aa1 { get; set; }
public int aa2 { get; set; }
}
//----------------Destination Object End Here---------------------
CreateMap<A, Simple>()
.ForMember(dest => dest.aa1, opt => opt.MapFrom(src => src.a1))
.ForMember(dest => dest.aa2, opt => opt.MapFrom(src => src.a2))
// Mapper IS NOT AVAILABLE HERE AS I AM USING PROFILE BASED CONFIGURATION
CreateMap<Complex, Simple>()
.ConvertUsing(src => Mapper.Map<A, Simple>(src.A)); <------Error at this line
//----------------Automammer config End Here---------------------
How to flatten from Complex to Simple? I don't wish to map Complex.A to Simple one by one again in the Complex to Simple config as it is already configured above.
Finally, I figured out with another overloaded method of ConvertUsing
CreateMap<Complex, Simple>()
.ConvertUsing((src,ctx) => {
return ctx.Mapper.Map<Complex, Simple>(src.A)
});
I feel this overloaded method has quite a multiple possibilities and flexibility. I don't have further issue of accessing Mapper directly as mentioned in the question. This overloaded method has its own context parameter (ResolutionContext). We can use Mapper from this context parameter like ctx.Mapper.Map<Complex, Simple>(src.A)

Automapper before/after map callbacks during mapping

I'm using ASP.NET Core and Automapper 6.1.0 ,
I have two types that look like this
public class ExampleDTO
{
public Guid Id { get; set; }
public ProviderDTO Provider { get; set; }
}
public class Example
{
public Guid Id { get; set; }
public Guid Provider { get; set; }
}
ProviderDTO class (which is irelevant in this case)
public class ProviderDTO
{
public Guid Id { get; set; }
public string Name { get; set; }
}
AutoMapper configuration looks like this:
CreateMap<Example, ExampleDTO>().ForMember(x => x.Provider, opt => opt.Ignore());
CreateMap<ExampleDTO, Example>().ForMember(dest => dest.Provider,
opt => opt.MapFrom(src => src.Provider.Id));
When I map from Example to ExampleDTO, I want to pass the value for ProviderDTO type.
I tried something like this.
_mapper.Map<ExampleDTO>(example, opt => opt.AfterMap((src, dest) => dest.Provider = myProvider));
I get this
'object' does not contain a defenition for 'Provider' and no extension method
Is this achievable? If yes, what am I doing wrong?
With the AutoMapper, you may need to provide both the source and destination type, such as:
_mapper.Map<Example, ExampleDTO>(example, opt => {
opt.AfterMap((src, dest) => dest.Provider = myProvider))
});

How to use automapper with different properties names? [duplicate]

I am a newbie to the Automapper framework. I have a domain class and a DTO class as follows:
public class Employee
{
public long Id {get;set;}
public string Name {get;set;}
public string Phone {get;set;}
public string Fax {get;set;}
public DateTime DateOfBirth {get;set;}
}
public class EmployeeDto
{
public long Id {get;set;}
public string FullName {get;set;}
public DateTime DateOfBirth {get;set;}
}
Note: The name of property "Name" of Employee class is not the same as that of property "FullName" of EmployeeDto class.
And here's the code to map the Employee object to EmployeeDto:
Mapper.CreateMap<Employee, EmployeeDto>(); // code line (***)
EmployeeDto dto = Mapper.Map<Employee, EmployeeDto>(employee);
My question is: If I want to map Employee (source class) to EmployeeDto (destination class), how can I specify the mapping rule? In other words, how should I do more with code line (***) above?
Never mind, I myself found a solution:
Mapper.CreateMap<Employee, EmployeeDto>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name));
Just to roll the comments above into an updated approach using Automapper 8.1+...
var mapConfig = new MapperConfiguration(
cfg => cfg.CreateMap<Employee, EmployeeDto>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name))
);
Then you would build the mapper using the mapConfig:
var mapper = mapConfig.CreateMapper();
We can also specify on Class attributes for mapping
From https://docs.automapper.org/en/stable/Conventions.html#attribute-support
Attribute Support
AddMemberConfiguration().AddName<SourceToDestinationNameMapperAttributesMember>();
* Currently is always on
Looks for instances of SourceToDestinationMapperAttribute for
Properties/Fields and calls user defined isMatch function to find
member matches.
MapToAttribute is one of them which will match the property based on
name provided.
public class Foo
{
[MapTo("SourceOfBar")]
public int Bar { get; set; }
}
Considering that we have two classes
public class LookupDetailsBO
{
public int ID { get; set; }
public string Description { get; set; }
}
and the other class is
public class MaterialBO
{
[MapTo(nameof(LookupDetailsBO.ID))]
public int MaterialId { get; set; }
[MapTo(nameof(LookupDetailsBO.Description))]
public string MaterialName { get; set; }
public int LanguageId { get; set; }
}
In this way you know typically to which property you follow .
and you make sure of the naming convention , so if you have changed the propery name in the source . The MapTo() will prompt an error
The new style of Attribute Mapping via Data Annotations:
https://docs.automapper.org/en/v8.1.0/Attribute-mapping.html?highlight=annotation
[AutoMap(typeof(Order))]
public class OrderDto {
// This is equivalent to a CreateMap<Order, OrderDto>()
For mapping the member
[SourceMember(nameof(Order.OrderTotal))]
public decimal Total { get; set; }
If you want reverse map then you add that property in
[AutoMap(typeof(Order), ReverseMap = true )]
public class OrderDto {
// This is equivalent to a CreateMap<Order, OrderDto>().ReverseMap()
The above answers are great and hope OP has got his answer. I just want to add how we can map fixed values instead of fields using UseValue() method of IMemberConfigurationExpression Interface.
Mapper.CreateMap<Employee, EmployeeDto>()
.ForMember(dest => dest.Department, opt => opt.UseValue("Development"));
This will map "Development" as a Department property value for destination data.

Need help with AutoMapper

I have two classes that looks as follows:
public class Rule
{
public int Id { get; set; }
public RuleGroup RuleGroup { get; set; }
}
public class RuleGroup
{
public int Id { get; set; }
public List<Rule> RuleList { get; set; }
}
A RuleGroup has a list of rules. My AutoMapper settings are as follows:
Mapper.CreateMap<RuleRecord, FirstSolar.Mes.Core.Entities.Recipe.Rule>()
.ForMember(destination => destination.RuleGroup, source => source.Ignore())
.ForMember(destination => destination.Id, source => source.MapFrom(item => item.RuleId));
Mapper.CreateMap<IList<RuleRecord>, IList<FirstSolar.Mes.Core.Entities.Recipe.Rule>>();
Mapper.CreateMap<RuleGroupRecord, FirstSolar.Mes.Core.Entities.Recipe.RuleGroup>()
.ForMember(destination => destination.Id, source => source.MapFrom(item => item.RuleGroupId));
Mapper.CreateMap<IList<RuleGroupRecord>, IList<FirstSolar.Mes.Core.Entities.Recipe.RuleGroup>>();
When I attempt to map a RuleGroupRecord (LinqToSQL object) to RuleGroup (DTO), AutoMapper says I need to add a mapping for RuleGroup.RuleList. I'm wondering why because I defined how to map a single RuleRecord and a List.
If I have to, how would I do it?
Simply add (I hope I got the syntax right, but you should see what I'm hinting at):
.ForMember(destination => destination.RuleList, source => source.MapFrom(item => item.Rules));
to the second mapping. While you handled the general mapping for RuleRecord to Rule in the first mapping, you didn't tell automapper to map the specific property RuleGroup.RuleList.

Categories

Resources