AutoMapper auto create createMap - c#

I have a services that is calling another services. Both of the services are using "the same classes". The classes are named same and have the same properties but has different namespace so I need to use AutoMapper to map from one of the type to the other type.
No it's pretty simple since all I have to do is the CreateMap<>, but the problem is that we have around hundreds of classes that I manually needs to write the CreateMap<> from, and it's works wired to me. Isn't there any Auto CreateMap function. So if I say CreateMap() then AutoMapper workes thru Organisation and finds all classes and automatically does the CreateMap for these Classes and it's subclasses etc etc…
Hope for a simple solution, or I guess some reflection can fix it...

Just set CreateMissingTypeMaps to true in the options:
var dto = Mapper.Map<FooDTO>
(foo, opts => opts.CreateMissingTypeMaps = true);
If you need to use it often, store the lambda in a delegate field:
static readonly Action<IMappingOperationOptions> _mapperOptions =
opts => opts.CreateMissingTypeMaps = true;
...
var dto = Mapper.Map<FooDTO>(foo, _mapperOptions);
UPDATE:
The approach described above no longer works in recent versions of AutoMapper.
Instead, you should create a mapper configuration with CreateMissingTypeMaps set to true and create a mapper instance from this configuration:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = true;
// other configurations
});
var mapper = config.CreateMapper();
If you want to keep using the old static API (no longer recommended), you can also do this:
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
// other configurations
});
UPDATE 2 - Automapper 9 and later:
Starting from Automapper version 9.0, the CreateMissingTypeMaps API was removed. Automapper documentation now suggests to explicitly configure maps, manually or using reflection.
https://docs.automapper.org/en/stable/9.0-Upgrade-Guide.html#automapper-no-longer-creates-maps-automatically-createmissingtypemaps-and-conventions

CreateMissingTypeMaps can be set within your profile. It's however recommended to explicitly use CreateMap for each mapping and call AssertConfigurationIsValid in your unit tests for each profile to prevent silent errors.
public class MyProfile : Profile {
CreateMissingTypeMaps = true;
// Mappings...
}

AutoMapper has a DynamicMap method which you might be able to use: here's an example unit test illustrating it.
[TestClass]
public class AutoMapper_Example
{
[TestMethod]
public void AutoMapper_DynamicMap()
{
Source source = new Source {Id = 1, Name = "Mr FooBar"};
Target target = Mapper.DynamicMap<Target>(source);
Assert.AreEqual(1, target.Id);
Assert.AreEqual("Mr FooBar", target.Name);
}
private class Target
{
public int Id { get; set; }
public string Name { get; set; }
}
private class Source
{
public int Id { get; set; }
public string Name { get; set; }
}
}

Set CreateMissingTypeMaps option to true. This is package AutoMapper.Extensions.Microsoft.DependencyInjection's example for ASP.NET Core:
public class Startup {
//...
public void ConfigureServices(IServiceCollection services) {
//...
services.AddAutoMapper(cfg => { cfg.CreateMissingTypeMaps = true; });
//...
}
//...
}

In case someone is still interested in this topic, I've created a NuGet package that gives the automatic mapping functionality since AutoMapper removed it in a certain version.
It's available under wakiter.AutoMapper.Extensions name.
To use it, invoke the CreateAutoMap extension method and it'll do the work for you.

Today I needed this in some generic code as well. I tried something like this:
private static IMapper CreateMapper<T1, T2>()
{
return new MapperConfiguration(cfg => FillMapperConfig(cfg, typeof(T1), typeof(T2)))
.CreateMapper();
}
private static void FillMapperConfig(IMapperConfigurationExpression cfg, Type T1, Type T2)
{
if (T1 == T2)
{
return;
}
cfg.CreateMap(T1, T2);
foreach (PropertyInfo propertyInfo in T1.GetProperties())
{
PropertyInfo correspondingProperty =
T2.GetProperties()
.FirstOrDefault(p =>
p.Name == propertyInfo.Name);
if (correspondingProperty != null)
{
if (propertyInfo.PropertyType.IsGenericType &&
correspondingProperty.PropertyType.IsGenericType)
{
FillMapperConfig(
cfg,
propertyInfo.PropertyType.GetGenericArguments()[0],
correspondingProperty.PropertyType.GetGenericArguments()[0]);
}
else if (propertyInfo.PropertyType.IsClass &&
correspondingProperty.PropertyType.IsClass)
{
FillMapperConfig(
cfg,
propertyInfo.PropertyType,
correspondingProperty.PropertyType);
}
}
}
}
Then I can do something like this:
IMapper mapper = CreateMapper<ClassA, ClassB>();
Which creates a map from ClassA to ClassB with all sub properties of ClassA and ClassB if they have the same name and recursively for sub sub properties.
Example:
public class ClassA {
public int IntProperty { get; set; }
public ClassASubProperty SubProperty { get; set; }
public List<ClassAListItem> ListItems { get; set; }
}
public class ClassB {
public int IntProperty { get; set; }
public ClassBSubProperty SubProperty { get; set; }
public List<ClassBListItem> ListItems { get; set; }
}
This should result in the IMapper equivalent:
new MapperConfiguration(cfg => {
cfg.CreateMap<ClassA, ClassB>();
cfg.CreateMap<ClassASubProperty, ClassBSubProperty>();
cfg.CreateMap<ClassAListItem, ClassBListItem>()
}).CreateMapper();

Related

c# automapper lookup value from source model

I have a model which has a generic properties property looking something like this:
public class GenericProperty
{
public string Name { get; set; }
public object Value { get; set; }
}
Next to that I have a object that has a list with GenericProperties like this:
public class GenericEntity
{
public string Name { get; set; }
public List<GenericProperty> Properties { get; set; }
}
Now im calling a API that deserialize the json to the model above. Next i want to use AutoMapper to construct an actual good looking model so i did the following:
Mapper.Initialize(x =>
{
x.CreateMap<GenericEntity, MyModel>()
.ForMember(d => d.ManagerId, o => o.MapFrom(s => s.Properties.Where(n => n.Name == "ManagerId" ).Select(v => v.Value)))
});
Problem is that this returns the property type and not the actual value:
System.Linq.Enumerable+WhereListIterator`1[MyProject.Models.GenericProperty]
How can i lookup the value of within the model?
By using Where you are selecting all the properties with the name ManagerId. You should instead use Single like in this unit test:
public class TestClass
{
[Test]
public void TestMapper()
{
Mapper.Initialize(x =>
{
x.CreateMap<GenericEntity, MyModel>()
.ForMember(d => d.ManagerId,
o => o.MapFrom(s => s.Properties.Single(n => n.Name == "ManagerId").Value));
});
var ge = new GenericEntity
{
Properties = new List<GenericProperty>
{
new GenericProperty {Name = "ManagerId", Value = "Great"}
}
};
var myModel = Mapper.Map<MyModel>(ge);
Assert.AreEqual("Great", myModel.ManagerId);
}
}
If you can not guarantee that there will be a property with the name ManagerId, you should use SingleOrDefault, and handle the null case.
IMHO, you should limit this kind of AutoMapper configurations to simple cases, since it quickly gets difficult to debug and maintain.
For more complex mappings like this one, I would recommend you to make an explicit mapping method instead, which you can call like an extension method. Another option worth considering if you want to use Automapper are Custom value resolvers.

Get ignored properties in Entity Framework

I work on a framework with EF. I want to get all ignored properties of an entity to build some special queries. How can I do it?
public class Customer
{
public int Id { get; set; }
public DateTime BirthDate { get; set; }
public int Age { get; set; }
}
public class CustomerContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().Ignore(customer => customer.Age);
base.OnModelCreating(modelBuilder);
}
public DbSet<Customer> Customers { get; set; }
}
public static class DbContextExtensions
{
public static List<string> GetIgnoredProperties(this DbContext context, string entityTypeName)
{
// ???
}
}
I know this is not answering your original question, and in my comments I mentioned that you should use reflection, but that was only because I read your question wrong.
Here is an alternative using reflection, for if you do not come right.
If you assign the [NotMapped] attribute to the properties on your class that you would like to ignore, you could possibly retrieve all [NotMapped] properties using reflection. Below is an example of how this could be achieved.
var resultArray = yourClassInstance.GetType().GetProperties()
.Where(prop => Attribute.IsDefined(prop, typeof(NotMappedAttribute)));
Hope this helps you in some way.
You can achieve what you want by calling the DbModelBuilder.Build. It will create a DbModel base on configuration setup by the DbModelBuilder. The DbModel expose a ConceptualModel that hold the types used by the context. The EdmModel hold each type that are declared in the context, and for each type, it hold the properties that has not been ignored by the DbModelBuilder during it's configuration. So, to achieve what you want, you have to intersect the properties of each entity type with those present in the EdmModel. It will give the delta between them, thefore the ignored properties. Here an example :
public class CustomerContext : DbContext
{
private static IReadOnlyDictionary<Type, IReadOnlyCollection<PropertyInfo>> _ignoredProperties;
/// Hold the ignored properties configured from fluent mapping
public static IReadOnlyDictionary<Type, IReadOnlyCollection<PropertyInfo>> IgnoredProperties
{
get
{
return _ignoredProperties;
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().Ignore(customer => customer.Age);
// Build ignored properties only if they are not
if (_ignoredProperties == null)
{
var model = modelBuilder.Build(this.Database.Connection);
var mappedEntityTypes = new Dictionary<Type, IReadOnlyCollection<PropertyInfo>>();
foreach (var entityType in model.ConceptualModel.EntityTypes)
{
var type = Type.GetType(entityType.FullName);
var typeProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var mappedProperties = entityType.DeclaredProperties.Select(t => t.Name)
.Union(entityType.NavigationProperties.Select(t => t.Name));
mappedEntityTypes.Add(type, new ReadOnlyCollection<PropertyInfo>(
typeProperties.Where(t => !mappedProperties.Contains(t.Name)).ToList()));
}
_ignoredProperties = new ReadOnlyDictionary<Type, IReadOnlyCollection<PropertyInfo>>(mappedEntityTypes);
}
base.OnModelCreating(modelBuilder);
}
public DbSet<Customer> Customers { get; set; }
}
The IgnoreProperties property is a singleton that will be initialized the first time you will use the context. It will be null before that, so will have to ensure that nothing use it until it's initialized. It's readonly, so you don't have to worrie about accidental clear of the collection. The entity type is used as key, and the value expose a collection that hold ignored properties. Example of use :
var properties = CustomerContext.IgnoredProperties[typeof(Customer)];
Cons :
With this approach is that the DbModel will be built twice, one time to gather the ignored properties, and second time by EntityFramework when the DbCompiledModel will be cached for futur ObjectContext creation. It can have an impact on the cold start of the DbContext, it means that the fist time you will execute a query over your context, it will be a bit slower. It will depend on the size of the DbContext. Warm queries should not suffer. OnModelCreating will be called once anyway.
Pros :
All changes made on de DbModelBuilder configuration will be automatically reflected in the IgnoredProperties property.

Reflection on mocked data in unit tests (Rhino)

I have some code that selects a property using reflection. It works fine but I can't unit test it. Apparently, the mock that it creates does not have any properties.
Can anyone tell me how to get the property from a Mocked object using reflection? (Or explain why it would not be possible?)
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
I'm using this for a copy class which allows to copy data for any entity type and expects a UnitOfWork that contains a IRepository where T is the type that you want to copy.
public class TableCopier<T> : ICopier where T : EntityBase
{
public IUnitOfWork From { get; set; }
public IUnitOfWork To { get; set; }
public virtual int Copy(IProgressReporter progres = null)
{
lock (this)
{
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
//...
}
}
}
The Unit of Work looks like this:
public interface IUnitOfWork
{
IRepository<Parameter> ParameterRepository { get; }
IRepository<Company> CompanyRepository { get; }
IRepository<CompanyAccountancynumber> CompanyAccountancynumberRepository { get; }
//...
And finally the unit test:
[Test]
public void DefaultCopy()
{
// (removed some code)
var to = MockRepository.GenerateStrictMock<IUnitOfWork>();
var target = MockRepository.GenerateStrictMock<IRepository<CompanyDataset>>();
to.Stub(x => x.CompanyDatasetRepository).Return(target);
var inserted = new List<CompanyDataset>();
target.Stub(x => x.BulkInsert(null))
.IgnoreArguments()
.WhenCalled(x => inserted.AddRange(x.Arguments[0] as IEnumerable<CompanyDataset>));
//ACT
var copier = new TableCopier<CompanyDataset>() as ICopier;
copier.Copy(from, to, map, log, null, CancellationToken.None );
I found the problem:
The properties are available as explicit properties.
And to access these kind of properties you need the interface map.
This means I should change this:
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
To this:
var target = To.GetType().GetInterfaceMap(typeof(IUnitOfWork))
.InterfaceMethods
.Single(x => (typeof(IRepository<T>).IsAssignableFrom(x.ReturnType)))
.Invoke(To, null) as IRepository<T>;
My own implementations of IUnitOfWork used 'implicit' properties, so that explains why that worked and the unit test failed. Also using the map is generally better (/more correct) (-> no chance on problems if I would ever make another implementation of the interface with explicit props). So I suppose it was a good unit test :)
Regards

AutoMapper TwoWay Mapping with same Property Name

Given these two objects
public class UserModel
{
public string Name {get;set;}
public IList<RoleModel> Roles {get;set;}
}
public class UserViewModel
{
public string Name {get;set;}
public IList<RoleViewModel> Roles {get;set;} // notice the ViewModel
}
Is this the most optimal way to do the mapping, or is AutoMapper capable of mapping Roles to Roles on its own?
App Config
Mapper.CreateMap<UserModel, UserViewModel>()
.ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles));
Mapper.CreateMap<UserViewModel, UserModel>()
.ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles));
Implementation
_userRepository.Create(Mapper.Map<UserModel>(someUserViewModelWithRolesAttached);
Is this the most optimal way to do the mapping, or is AutoMapper capable of mapping Roles to Roles on its own?
If the property names are identical, you should not have to manually provide a mapping:
Mapper.CreateMap<UserModel, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserModel>();
Just make sure the inner types are mapped as well (RoleViewModel ↔ RoleModel)
What this means, however, is that if you change a source or destination property name, AutoMapper mappings can fail silently and cause hard to track down problems (e.g., if you changed UserModel.Roles to UserModel.RolesCollection without changing UserViewModels.Roles).
AutoMapper provides a Mapper.AssertConfigurationIsValid() method that will check all of your mappings for errors and catch misconfigured mappings. It's useful to have a unit test that runs with the build that validates your mappings for this kind of problem.
You don't need to map the properties. Just make sure that the property names match and there is a mapping defined between them.
Mapper.CreateMap<UserModel, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserModel>();
Mapper.CreateMap<RoleModel, RoleViewModel>();
Mapper.CreateMap<RoleViewModel, RoleModel>();
Or with the cooler way I just found out:
Mapper.CreateMap<UserModel, UserViewModel>().ReverseMap();
Mapper.CreateMap<RoleModel, RoleViewModel>().ReverseMap();
All the other answers, are much better (which I gave an upvote to each).
But what I wanted to post here is a quick playground that you could copy and past right into LinqPad in C# program mode and play your idea's without messing with your actual code.
Another awesome thing about moving all your conversions into a TyperConverter class is that your conversions are now Unit Testable. :)
Here you will notice that the model and viewmodel are almost identical except for one property. But through this process the right property is converted to the correct property in the destination object.
Copy this code into LinqPad and you can run it with the play button after switching to C# Program mode.
void Main()
{
AutoMapper.Mapper.CreateMap<UserModel, UserViewModel>().ConvertUsing(new UserModelToUserViewModelConverter());
AutoMapper.Mapper.AssertConfigurationIsValid();
var userModel = new UserModel
{
DifferentPropertyName = "Batman",
Name = "RockStar",
Roles = new[] {new RoleModel(), new RoleModel() }
};
var userViewModel = AutoMapper.Mapper.Map<UserViewModel>(userModel);
Console.WriteLine(userViewModel.ToString());
}
// Define other methods and classes here
public class UserModel
{
public string Name {get;set;}
public IEnumerable<RoleModel> Roles { get; set; }
public string DifferentPropertyName { get; set; }
}
public class UserViewModel
{
public string Name {get;set;}
public IEnumerable<RoleModel> Roles { get; set; } // notice the ViewModel
public string Thingy { get; set; }
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine(string.Format("Name: {0}", Name));
sb.AppendLine(string.Format("Thingy: {0}", Thingy));
sb.AppendLine(string.Format("Contains #{0} of roles", Roles.Count()));
return sb.ToString();
}
}
public class UserModelToUserViewModelConverter : TypeConverter<UserModel, UserViewModel>
{
protected override UserViewModel ConvertCore(UserModel source)
{
if(source == null)
{
return null;
}
//You can add logic here to deal with nulls, empty strings, empty objects etc
var userViewModel = new UserViewModel
{
Name = source.Name,
Roles = source.Roles,
Thingy = source.DifferentPropertyName
};
return userViewModel;
}
}
public class RoleModel
{
//no content for ease, plus this has it's own mapper in real life
}
Result from the Console.WriteLine(userViewModel.ToString());:
Name: RockStar
Thingy: Batman
Contains #2 of roles
Inside the Startup.cs in the Configure() method:
Mapper.Initialize(config => {
config.CreateMap<UserModel, UserViewModel>().ReverseMap();
// other maps you want to do.
});

AutoMapper Flattening an Extension method

I'm looking for the simplest / most elegant way to flatten a source object utilizing extension methods of the source object.
Source:
class Source
{
public int Value1 { get; set; }
public int Value2 { get; set; }
}
Extension method I'd like to elegantly map:
static class SourceExtensions
{
public static int GetTotal(this Source source)
{
return source.Value1 + source.Value2;
}
}
Destination:
class Destination
{
public int Value1 { get; set; }
public int Value2 { get; set; }
public int Total { get; set; }
}
Is there a better way than this (one where I don't have to call out every extension method)?
using NamespaceContainingMyExtensionMethods;
...
Mapper.CreateMap<Source, Destination>()
.ForMember(destination => destination.Total,
opt => opt.ResolveUsing(source => source.GetTotal()));
Something like:
Mapper.CreateMap<Source, Destination>()
.ResolveUsingExtensionsInNamespace("NamespaceContainingMyExtensionMethods");
I know I can use an inheritance heirarchy on the source objects, but in my situation, it isn't ideal.
I've researched:
Does AutoMapper's convention based mappings work with LINQ extension methods? and https://github.com/AutoMapper/AutoMapper/issues/34
Added commit to my fork and make a pull request for this. Works like a charm!
commit: https://github.com/claycephus/AutoMapper/commit/e1aaf9421c63fb15daca02607d0fc3dff871fbd1
pull request: https://github.com/AutoMapper/AutoMapper/pull/221
Configure it by specifying assemblies to search:
Assembly[] extensionMethodSearch = new Assembly[] { Assembly.Load("Your.Assembly") };
Mapper.Initialize(config => config.SourceExtensionMethodSearch = extensionMethodSearch);
Mapper.CreateMap<Source, Destination>();
Clay stuff is still there but it has been refactored over time. For those searching for this in automapper, you should use:
var config = new MapperConfiguration(cfg => {
cfg.IncludeSourceExtensionMethods(typeof(SourceExtensions));
cfg.CreateMap<Source, Destination>();
});
var mapper = config.CreateMapper();

Categories

Resources