Is there a way to dyanmically create multiple maps for Automapper? - c#

Is there a way to dynamically generate multiple maps? For example, can I map to an array of source and destination values? I am working with a legacy database and one of the tables has over 350+ columns. It will be very error prone and labor intensive to map each field manually.
//HOW CAN BELOW INCLUDE OVER 350+ MAPPED FIELDS DYNAMICALLY FROM AN ARRAY?
CreateMap<Employee, EditEmployeeModel>()
.ForMember(dest => dest.ConfirmEmail,
opt => opt.MapFrom(src => src.Email));

Basically if the property names are similar then you don't have to map source to destination manually. Automapper will do this automatically. So, if you keep the property names similar then there's no need to map.
But if that's not possible and keeping different names is a necessity then you can use .AfterMap() feature of Automapper. It is simpler than source destination mapping.
public class EditEmployeeMap : IMappingAction<Employee, EditEmployeeModel>
{
public void Process(Employee source, EditEmployeeModel destination, ResolutionContext context)
{
destination.ConfirmEmail = source.Email;
}
}
Then,
CreateMap<Employee, EditEmployeeModel>().AfterMap<EditEmployeeMap>();
Though the purpose of the above solution is not for this use case, this can make the job more simple for you.
But i would suggest to keep the property name similar, that will save a lot of hastle.

Related

Automapper Collection

I'm working on a project and I need to map collections. I came across Automapper.Collection and am trying to user the class but its all not working. Please I need help. Here is my code.
In my startup class
services.AddAutoMapper(cfg => { cfg.AddCollectionMappers(); },typeof(Startup));
I also created a class that Inherits from the Automapper Profile class here
public class UserMappingProfile : Profile
{
public UserMappingProfile()
{
CreateMap<PhotoToUpdateDto, Photo>().EqualityComparison((src, dest) => src.PublicId.ToLower() == dest.PublicId.ToLower());
CreateMap<SocialHandles, SocialHandleDto>().ReverseMap().EqualityComparison((src, dest) => src.Name.ToLower() == dest.Name.ToLower());
}
}
Anytime I use the mapper, it creates new records in the database instead of updating the already existing. Please I need help.
I think its because of navigation property in your entity
If you have navigation property ignore them in your Createmap
For example
CreateMap<PhotoToUpdateDto, Photo>().EqualityComparison((src, dest) =>
src.PublicId.ToLower() == dest.PublicId.ToLower())
.ForMember(p => p.Photographer , opt => opt.Ignore());
I'll assume this:
You have no problem with the mapper (since it still map your object successfully).
When you get new object to interact with database, a new record created, since your make sure the id mapping was correct.
You are using EF Core or some kind of ORM to interact with the database
If i was wrong, please just ignore this answer.
Okay, If my assuming was right, that's because automapper always return a new object as result of the mapping process, which is absolutely not tracked by your DbContext yet. And then, whenever you interact with that to make some change in the database, an add operation is likely going to happen.
I think you need to add some more information about the block of code that you actually have problem with... which in this case... why the database entry keep created, but not update.

Update only mapped fields or Ignore the unmapped fields - Automapper?

I'm very new to Automapper and I'm facing difficulties in Updating mapped fields. I must agree that there are millions of answers for this question in internet but the problem is nothing helps for my scenario.
Issue:
I'm trying to update only the mapped fields. But when I do that, my complete destination properties gets updated.
For e.g.,
My destination object(DB Table) holds 5 properties and all the 5
properties accepts null. But I have only 3 properties in my
source(Model file). I'm mapping all the three source fields with the
destination fields but I don't have mapping for the remaining two
destination properties. You would have guessed it correctly, yes when
I update this, that two unmapped fields are also getting update to null(default value - DB). But it
should not be the case, rather it should have the existing values.
Kindly see my code below,
References:
Automapper Version: v6.2.2 and Runtime Version: v4.0.3
Repository.cs,
var objectToUpdate = Mapper.Map<TDomain>(entity); //TDestination Map<TDestination>(object source);
DatabaseContext.Entry(objectToUpdate).State = EntityState.Modified;
InvoiceLineMapping.cs
public static void Map(IProfileExpression profile)
{
profile.CreateMap<Registration, PT_Registration>()
.ForMember(d => d.Name_VC, map => map.MapFrom(s => s.Name))
.ForMember(d => d.UserName_VC, map => map.MapFrom(s => s.Username))
.ForMember(d => d.Mobile_VC, map => map.MapFrom(s => s.Mobile));
}
Note: As I said before, I have two unmapped fields are there in the destination and they are EMail_VC and Comments_VC and their values are sample#test.com and testComments respectively. When update happens, these two fields becomes null(default value)
I tried,
.ForAllOtherMembers(opt => opt.Ignore()); //after the last mapping i.e Mobile
profile.CreateMap(MemberList.Source) // I also tried MemberList.Destination
I also tried, AutoMapper: "Ignore the rest"? extenstion method,
I tried puttin ReverseMap() in the end. //after the last mapping i.e Mobile
I even tried to find the null value and removing from destination object(objectToUpdate - you can see above) but nothing helps.
So can someone please look into this and provide me a better solution. I knew solution would be simpler but as I'm not aware of it, it takes more and more time.
I appreciate your time. Any help would be great helpful.

AutoMapper: ignore property on update existing, but allow when create

Is it possible to configure/use AutoMapper in such a way where when i create an object from a mapping i allow all properties and child collections, however, when it comes to performing an update to existing object, the mapping will ignore child collection properties as they will be empty but i dont want them removed.
This is because i am working with a WCF service that sends delta changes to objects and most of my model works in a tree hierarchy:
Parent
List<Child> Children
ParentDto
List<ChildDto> Children
config.CreateMap<ParentDto, Parent>();
config.CreateMap<ChildDto, ChildDto>();
This works well and the child collection is populated first time round. However, there are scenarios where i will send the ParentDto across with just the parent POCO property changes (such as a datetime change), but the child list will be empty as none of them have changed. Normally i would do:
_Mapper.Map<ParentDto,Parent>(dto, local)
but obviously that will change the entire tree and populate the local object with an empty child list. Massively simplifying but would something like
_Mapper.Map<ParentDto, Parent>(dto, local).Ignore(p => p.Children)
be possible?
I should also add I am using SimpleInjector DI framework. So perhaps there is a way to register 2 configurations, one with ignore and one without?
Use .ForMember(dest => dest.A, opt => opt.MapFrom(src => src.B)) for mapping only properties you need to update.
For those who still struggle to find this. You can use Autommapper Conditional Mapping.
You can do it like this, in the Initialize
config.CreateMap<ChildDto, ChildDto>().ForMember(dest => dest.Children, opt => opt.Condition(source => source.TriggerChildMap));
This will ignore mapping based on the property in source object. To map against existing destination you need to use
Mapper.Map(source, destination) method and not the var result = Mapper.Map<ChildDto>(source) property.

Is it possible to use same ClassMap with different DB schemas in NHibernate?

Currently I have two databases of identical structure (one is for temp changes, another is live one). I use same NHibernate entities, mappings and repositories to access both databases. Only connection string is changed when creating Session.
Now I need to change approach and merge those two DBs into one. I'm planning to separate equally named tables by introducing different schemas for them. Sounds easy. But the problem arised when I checked how to update my NHibernate mappings so that they support either one schema or another - it seems that map classes support only parameterless constructors (at least all methods to add mappings to NHibernate configuration doesn't suppose any constructor parameters).
Here is some sample code to demonstrate what I'd like to achieve:
public class MyEntityMap : ClassMap<MyEntity>
{
public MyEntityMap ()
{
Table("MyEntities");
Schema("a"); //I can specify schema as a constant here, but I need it to be variable: constructor either parameter or changable in other way
Id(x => x.Id, "Id");
Map(x => x.Name, "Name").Length(100);
}
}
When creating NHibernate configuration there are the following options to add mappings to it (three method calls are shown just for options, I use single call to AddFromAssemblyOf currently):
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString("MyConnectionString"))
.Mappings(m => m.FluentMappings.AddFromAssembly(typeof (MyEntityMap).Assembly))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<MyEntityMap>())
.Mappings(m => m.FluentMappings.Add<MyEntityMap>)
Obviously neither option supports parameterized constructor of ClassMap derived classes and I can't see how else can I specify schema for all mappings at once.
Solutions I see (but which seem to be an overkill):
Manually create 2 derived classes from each mapping class and place them in different assemblies. So that each class provide it's value for schema constant.
Autogenerate 2 mapping classes, derived from each 'base' mapping using System.Emit. Use assemblies, generated at runtime to pass to AddMappingFromAssembly method.
Am I missing something? Is there an easy way to specify which schema to use for all repositories, created using specified NHibernate configuration?
Rather than adapting all the mappings, why not use default_schema configuration parameter?
Or have I missed something causing such a global switch to be inadequate for your case?
Of course this will require to cease specifying in your mapping which schema to use on each table having to switch schema.
To configure this setting by code, you may add it through:
var configuration = new NHibernate.Cfg.Configuration();
...
configuration.AddProperties(
new Dictionary<string, string>
{
{ NHibernate.Cfg.Environment.DefaultSchema, "yourDbName.yourSchema" }
});
...
var sessionFactory = configuration.BuildSessionFactory();
Or set them with:
configuration.Properties[NHibernate.Cfg.Environment.DefaultSchema] =
"yourDbName.yourSchema";
Specifying db name is optional and not supported by all databases, but may improve performances with SQL-Server.

When using Entity Framework Code First, is it acceptable to add properties to a class that are not mapped to a database table at all?

Just as the title states, Can I add an extra property to one of my POCOs that does not map to a DB column(database was created first), and will not be persisted. This property will only be used within the application and never needs to be persisted.
Are there any extra measures to take to accomplish this besides defining the property as normal?
Yes, you absolutely can do that. Here's an example from a configuration class I have:
public class ForCommentEntities:EntityTypeConfiguration<Comment> {
public ForCommentEntities(String schemaName) {
this.HasRequired(e => e.SystemUser)
.WithMany()
.Map(m => m.MapKey("SystemUserID"));
this.Ignore(e => e.Remarks);
this.ToTable("Comment", schemaName);
}
}
The this.Ignore call is the important part. It takes a lambda expression to one of the properties on your class. This is part of what makes EFCF great (IMO) as it keeps configuration detail out of your POCOs.
The configuration class would be used like this in your Context:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
var schemaName = Properties.Settings.Default.SchemaName;
modelBuilder.Configurations
.Add(new Configuration.ForCommentEntities(schemaName))
// ...other configuration options here
;
}
The nice part about Code First and POCO is you can now have business objects which are used by EF without the need of a mapper (e.g. AutoMapper or your own). Also means not having to decorate your objects with EF attributes and more (hence Yuck's answer above). However a additional advantage is, yes, the ability to add methods or properties to the object. An example would be a collection (e.g. addresses) and you would like to have a sorted or filtered projection. Another would be business rule validation before calling SaveChange(). As we all know, the possibilities are endless but the point is you can and should use these objects as business objects who get populated from your data layer.

Categories

Resources