Automapper: How to leverage a custom INamingConvention? - automapper

I am working with a database where the designers really seemed to enjoy capital letters and the underscore key. Since I have a simple ORM, my data models use these names as well. I need to build DTOs and I would prefer to give them standard names since we are exposing them through services.
The code below is now corrected! The test passes so use this as a reference if you need to use multiple naming conventions
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using AutoMapper;
using NUnit.Framework;
namespace AutomapperTest
{
public class DATAMODEL
{
public Guid ID { get; set; }
public string FIRST_NAME { get; set; }
public List<CHILD_DATAMODEL> CHILDREN { get; set; }
}
public class CHILD_DATAMODEL
{
public Guid ID { get; set; }
public int ORDER_ID { get; set; }
}
public class DataModelDto
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public List<ChildDataModelDto> Children { get; set; }
}
public class ChildDataModelDto
{
public Guid Id { get; set; }
public int OrderId { get; set; }
}
public class UpperUnderscoreNamingConvention : INamingConvention
{
private readonly Regex _splittingExpression = new Regex(#"[\p{Lu}0-9]+(?=_?)");
public Regex SplittingExpression { get { return _splittingExpression; } }
public string SeparatorCharacter { get { return "_"; } }
}
public class Profile1 : Profile
{
protected override void Configure()
{
SourceMemberNamingConvention = new UpperUnderscoreNamingConvention();
DestinationMemberNamingConvention = new PascalCaseNamingConvention();
CreateMap<DATAMODEL, DataModelDto>();
CreateMap<CHILD_DATAMODEL, ChildDataModelDto>();
}
}
[TestFixture]
public class Tests
{
[Test]
public void CanMap()
{
//tell automapper to use my convention
Mapper.Initialize(x => x.AddProfile<Profile1>());
//make a dummy source object
var src = new DATAMODEL();
src.ID = Guid.NewGuid();
src.FIRST_NAME = "foobar";
src.CHILDREN = new List<CHILD_DATAMODEL>
{
new CHILD_DATAMODEL()
{
ID = Guid.NewGuid(),
ORDER_ID = 999
}
};
//map to destination
var dest = Mapper.Map<DATAMODEL, DataModelDto>(src);
Assert.AreEqual(src.ID, dest.Id);
Assert.AreEqual(src.FIRST_NAME, dest.FirstName);
Assert.AreEqual(src.CHILDREN.Count, dest.Children.Count);
Assert.AreEqual(src.CHILDREN[0].ID, dest.Children[0].Id);
Assert.AreEqual(src.CHILDREN[0].ORDER_ID, dest.Children[0].OrderId);
}
}
}

Create your mappings in profiles, and define the INamingConvention parameters as appropriate.
I don't like the global/static, so I prefer using Initialize and define all of my mappings together. This also has the added benefit of allowing a call to AssertConfiguration... which means if I've borked my mapping I'll get the exception at launch instead of whenever my code gets around to using the problematic mapping.
Mapper.Initialize(configuration =>
{
configuration.CreateProfile("Profile1", CreateProfile1);
configuration.CreateProfile("Profile2", CreateProfile2);
});
Mapper.AssertConfigurationIsValid();
in the same class with that initialization method:
public void CreateProfile1(IProfileExpression profile)
{
// this.CreateMap (not Mapper.CreateMap) statements that do the "normal" thing here
// equivalent to Mapper.CreateMap( ... ).WithProfile("Profile1");
}
public void CreateProfile2(IProfileExpression profile)
{
profile.SourceMemberNamingConvention = new PascalCaseNamingConvention();
profile.DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention();
// this.CreateMap (not Mapper.CreateMap) statements that need your special conventions here
// equivalent to Mapper.CreateMap( ... ).WithProfile("Profile2");
}
if you do it this way, and don't define the same mapping in both profiles, I don't think you need anything to "fill in the blank" from the original question, it should already be setup to do the right thing.

What about
public class DATAMODELProfile : Profile
{
protected override void Configure()
{
Mapper.CreateMap<DATAMODEL, DATAMODEL>();
Mapper.CreateMap<DATAMODEL, SOMETHINGELSE>();
Mapper.CreateMap<DATAMODEL, DataModelDto>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.ID))
.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.FIRST_NAME))
.ForMember(dest => dest.ChildDataModels, opt => opt.MapFrom(src => src.CHILD_DATAMODELS));
}
}

Related

inherited class AutoMapper.AutoMapperMappingException

I am new at automapper and it is a very good stuff easy to use, but now I have a problem with it. Trying to convert my derived class to base and it gives me
AutoMapper.AutoMapperMappingException
Missing type map configuration or unsupported mapping.
Mapping types: ClientEventDb -> EventId
Database.ClientEventDb -> EventId
Destination path: ClientEvent
Source value:
Event:Login
Automapper wants to convert ClientEventDb to EventId? I don't understand why. EventId is an enum...
Please help me I have run out of ideas.
Here is the code which I run:
ClientEventDb[] edbl;
using (var context = new DbEntities())
{
edbl=context.Events.Take(1000).ToArray();
}
Mapper.CreateMap<ClientEventDb, ClientEvent>();
Console.WriteLine("hello");
return edbl.Select(edb => Mapper.Map<ClientEvent>(edb)).ToArray();
Here are my classes
[Table("events", Schema = "public")]
public class ClientEventDb : ClientEvent
{
public ClientEventDb(string userName, EventId happening, object userObject = null)
: base(userName, happening, userObject)
{
}
public ClientEventDb()
{
}
}
[ProtoContract]
[Table("events", Schema = "public")]
public class ClientEvent : ClientEventBase
{
[ProtoMember(1)]
[Column("username")]
public string UserName { get; private set; }
[ProtoMember(2)]
[Column("time")]
public DateTime DateTime { get; private set; }
[ProtoMember(3)]
[Key]
[Column("id")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; private set; }
[ProtoMember(4)]
[Column("data")]
public byte[] UserObject { get; set; }
public ClientEvent(string userName,EventId happening, object userObject=null) : base(happening)
{
UserName = userName;
DateTime = DateTime.Now;
//UserObject = null;
if (userObject!=null) throw new NotImplementedException();
}
public ClientEvent()
{
}
protected ClientEvent Clone()
{
return (ClientEvent)MemberwiseClone();
}
}
[ProtoContract]
[ProtoInclude(10, typeof(ClientEvent))]
public class ClientEventBase
{
[Column("eventid")]
[ProtoMember(1)]
public int EventIdValue { get; set; } //must be public because of entity framework
[NotMapped]
public EventId EventId
{
get { return (EventId) EventIdValue; }
set { EventIdValue = (int) value; }
}
public ClientEventBase(EventId eventId)
{
EventId = eventId;
}
public ClientEventBase()
{
}
public override string ToString()
{
return String.Format("Event:{0}",EventId);
}
}
public enum EventId
{
Login = 1,
Logout,
ExitApplication,
}
UPDATE
bugfix: ClientEvent [Key] attribute moved to id property
Solution was this (thx to stuartd):
ClientEventDb[] edbl;
using (var context = new DbEntities())
{
edbl=context.Events.ToArray();
}
Mapper.CreateMap<ClientEventDb, ClientEvent>().ConstructUsing((ClientEventDb src) => new ClientEvent());
return edbl.Select(Mapper.Map<ClientEvent>).ToArray();
AutoMapper is confused as its made to map between similar properties in different classes, you are using it incorrectly - you just need to go from the derived class to the base which does not require AutoMapper. You could use this to do what you need....
ClientEventDb[] edbl;
using (var context = new DbEntities())
{
edbl=context.Events.Take(1000).ToArray();
}
return edbl.Cast<ClientEvent>().ToList();
I'd be looking at why you even feel you need a derived ClientEventDb though - understand we dont have the whole picture here but it seems to do nothing in addition to what the base class already does.
The issue is that ClientEvent has two constructors but you have not told AutoMapper which to use.
If you want it to use your constructor with parameters, change your mapping code to this and it will work:
Mapper.CreateMap<ClientEventDb, ClientEvent>()
.ConstructUsing(src => new ClientEvent(src.UserName, src.EventId));
Or to make AutoMapper use the default constructor:
Mapper.CreateMap<ClientEventDb, ClientEvent>()
.ConstructUsing((ClientEventDb src) => new ClientEvent());

adding initial rows into tables using Fluent migrator

Im a classic programmer that is newbie at generics and this is an asp.net MVC5 sample application for learning purposes of integrating authorization (users/roles) using fluent migrator lib. I wantto add some sample datas into tables as they created (using migrator console tool).
getting compilation error: USERNAME does not exist in the current context
what should I add in to using section or any example of:
Insert.IntoTable method ?
(thanks)
namespace SampleApp.Migrations
{
[Migration(1)]
public class AuthMigrations:Migration
{
public override void Up()
{
Create.Table("users").
WithColumn("ID").AsInt32().Identity().PrimaryKey().
WithColumn("USERNAME").AsString(128).
WithColumn("EMAIL").AsCustom("VARCHAR(128)").
WithColumn("PASSWORD_HASH").AsString(128);
Create.Table("roles").
WithColumn("ID").AsInt32().Identity().PrimaryKey().
WithColumn("NAME").AsString(128);
Create.Table("role_users").
WithColumn("ID").AsInt32().Identity().PrimaryKey().
WithColumn("USER_ID").AsInt32().ForeignKey("users", "ID").OnDelete(Rule.Cascade).
WithColumn("ROLE_ID").AsInt32().ForeignKey("roles", "ID").OnDelete(Rule.Cascade);
//Error:The name 'USERNAME' does not exist in the current context
Insert.IntoTable("users").Row(new { USERNAME:"superadmin",EMAIL:"superadmin#mvcapp.com",PASSWORD_HASH:"dfgkmdglkdmfg34532+"});
Insert.IntoTable("users").Row(new { USERNAME:"admin",EMAIL:"admin#mvcapp.com",PASSWORD_HASH:"dfgkmdglkdmfg34532+"});
}
public override void Down()
{
Delete.Table("role_users");
Delete.Table("roles");
Delete.Table("users");
}
}
and
namespace SampleApp.Models
{
public class User
{
public virtual int Id { get; set; }
public virtual string Username { get; set; }
public virtual string EMail { get; set; }
public virtual string passwordhash { get; set; }
}
public class UserMap : ClassMapping<User>
{
public UserMap()
{
Table("Users");
Id(x => x.Id, x => x.Generator(Generators.Identity));
Property(x => x.Username, x => x.NotNullable(true));
Property(x => x.EMail, x => x.NotNullable(true));
Property(x=>x.passwordhash,x=>
{
x.Column("PASSWORD_HASH");
x.NotNullable(true);
});
}
}
}
In C#, you must use an equals sign ("=") in the object initializer instead of a colon (":").
Insert.IntoTable("users").Row(new { USERNAME = "superadmin",EMAIL = "superadmin#mvcapp.com",PASSWORD_HASH = "dfgkmdglkdmfg34532+"});
Insert.IntoTable("users").Row(new { USERNAME = "admin",EMAIL = "admin#mvcapp.com",PASSWORD_HASH = "dfgkmdglkdmfg34532+"});

Nhibernate confused by class inheritance and returns mixed results

I have a class with a few properties and some methods
public class Content
{
public int Id { get; set; }
public string Application { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public override bool Equals(object obj) {...}
public override int GetHashCode() {...}
}
With this Fluent NHibernate mapping:
public class ContentMapping : ClassMap<Content>
{
public ContentMapping()
{
Table("vw_all_contents");
CompositeId()
.KeyProperty(x => x.Id, "id")
.KeyProperty(x => x.Application, "application");
Map(x => x.Property1, "property1");
Map(x => x.Property2, "property2");
}
}
Up to here everything works fine.
I now want to populate the same object but with a table a federated table that connects to another database.
So I have:
public class ContentOnProductionDatabase : Content { }
With a mapping:
public class ContenOnProductionDatabasetMapping : ClassMap<ContentOnProductionDatabase>
{
public ContentOnProductionDatabaseMapping()
{
Table("vw_federated_all_contents");
CompositeId()
.KeyProperty(x => x.Id, "id")
.KeyProperty(x => x.Application, "application");
Map(x => x.Property1, "property1");
Map(x => x.Property2, "property2");
}
}
And here is where NHibernate gets really confused and the queries return mixed results from both databases.
The problem goes away if my ContentOnProductionDatabase does not extend Content but instead is a duplicate class like this:
public class ContentOnProductionDatabaseMapping
{
public int Id { get; set; }
public string Application { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public override bool Equals(object obj) {...}
public override int GetHashCode() {...}
}
So now everything is fine but I don't like the fact that there is so much code duplication and it seems to me there must be some sort of Mapping configuration out there to force NHibernate to ignore the inheritance and differentiate the two, especially since they map to different databases.
The repository framework is an inbuilt one handles the session and the queries.
public class ContentRepository : NHibernateRepositoryBase, IContentRepository
{
public ContentRepository(INHibernateContext context, ISettingsManager settingsManager): base(context){ }
public Content ReadContent(int id, string application)
{
using (ISessionContainer container = Context.GetSessionContainer())
{
return
container.AsQueryable<Content>()
.FirstOrDefault(c => c.Id == id && c.Application == application);
// All queries using <Content> return the correct results
}
}
public ContentOnProductionDataBase ReadFederatedContent(int id, string application)
{
using (ISessionContainer container = Context.GetSessionContainer())
{
return
container.AsQueryable<ContentOnProductionDataBase>()
.FirstOrDefault(c => c.Id == id && c.Application == application);
// All queries using <ContentOnProductionDataBase> return the combined results of <Content> and <ContentOnProductionDataBase>
}
}
}
Internally the container.AsQueryable works by invoking this:
public IQueryable<TEntity> AsQueryable<TEntity>() where TEntity : class
{
return LinqExtensionMethods.Query<TEntity>(this.Session);
}
Any ideas how to get rid of the code duplication?
To define the class mapping and the properties only once, you have to define a base class and define the mapping with UseUnionSubclassForInheritanceMapping which will allow you to use independent tables per entity which is derived from that base class.
You don't have to but you should declare your base class as abstract, because it will not have a database representation. So persisting the base class will fail! Meaning, you don't want anyone to use it as an entity, instead use your derived classes...
To do so, create one base, and 2 derived classes which should be stored in one table per class.
public abstract class ContentBase
{
public virtual int Id { get; set; }
public virtual string Application { get; set; }
public virtual string Property1 { get; set; }
public virtual string Property2 { get; set; }
public override bool Equals(object obj)
{
[..]
}
public override int GetHashCode()
{
[..]
}
}
public class Content : ContentBase
{
}
public class ContentOnProductionDatabaset : ContentBase
{
}
The mapping of the base class must call UseUnionSubclassForInheritanceMapping, otherwise nHibernate would combine the classes.
public class ContentBaseMapping : ClassMap<ContentBase>
{
public ContentBaseMapping()
{
UseUnionSubclassForInheritanceMapping();
CompositeId()
.KeyProperty(x => x.Id, "id")
.KeyProperty(x => x.Application, "application");
Map(x => x.Property1, "property1");
Map(x => x.Property2, "property2");
}
}
The subclass mappings just have to define that the base is abstract.
Here you can also define each table name the entity should use.
public class ContentMapping : SubclassMap<Content>
{
public ContentMapping()
{
Table("vw_all_contents");
Abstract();
}
}
public class ContentOnProductionDatabaseMapping : SubclassMap<ContentOnProductionDatabaset>
{
public ContentOnProductionDatabaseMapping()
{
Table("vw_federated_all_contents");
Abstract();
}
}

Can AutoMapper implicitly flatten this mapping?

I am trying to map between two lists of objects. The source type has a complex property of type A; the destination type is a flattened subset of type A plus an additional scalar property that is in the source type.
public class A
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Source
{
public A MyA { get; set; }
public int SomeOtherValue { get; set; }
}
public class Destination
{
public string Name { get; set; }
public int SomeOtherValue { get; set; }
}
If it's not clear, I'd like Source.MyA.Name to map to Destination.Name and Source.SomeOtherValue to map to Destination.SomeOtherValue.
In reality, type A has a dozen or so properties, about which 80% map over to properties of the same name in Destination. I can get things to work if I explicitly spell out the mappings in CreateMap like so:
CreateMap<Source, Destination>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.MyA.Name));
The downside here is I want to avoid having to add a ForMember line for each of A's properties that need to get copied over to Destination. I was hoping I could do something like:
CreateMap<Source, Destination>()
.ForMember(dest => dest, opt => opt.MapFrom(src => src.MyA));
But if I try the above I get a runtime error when the mapping is registered: "Custom configuration for members is only supported for top-level individual members on a type."
Thanks
create mappings between A and Destination, and Source and Destination, and then use AfterMap() to use first mapping in second
Mapper.CreateMap<A, Destination>();
Mapper.CreateMap<Source, Destination>()
.AfterMap((s, d) => Mapper.Map<A, Destination>(s.MyA, d));
then use it like this:
var res = Mapper.Map<Source, Destination>(new Source { SomeOtherValue = 7, MyA = new A { Id = 1, Name = "SomeName" } });
As a workaround you can use custom type converter with additional property in the destination type to avoid recursion.
[TestFixture]
public class MapComplexType
{
[Test]
public void Map()
{
Mapper.CreateMap<A, Destination>();
Mapper.CreateMap<Source, Destination>().ConvertUsing(new TypeConvertor());
var source = new Source
{
MyA = new A
{
Name = "Name"
},
SomeOtherValue = 5
};
var dest = new Destination();
Mapper.Map(source, dest);
Assert.AreEqual(dest.Name, "Name");
}
}
public class TypeConvertor : ITypeConverter<Source, Destination>
{
public Destination Convert(ResolutionContext context)
{
var destination = (Destination) context.DestinationValue;
if (!((Destination)context.DestinationValue).IsMapped || destination == null)
{
destination = destination ?? new Destination();
destination.IsMapped = true; // To avoid recursion
Mapper.Map((Source)context.SourceValue, destination);
destination.IsMapped = false; // If you want to map the same object few times
}
Mapper.Map(((Source)context.SourceValue).MyA, destination);
return (Destination)context.DestinationValue;
}
}
public class A
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Source
{
public A MyA { get; set; }
public int SomeOtherValue { get; set; }
}
public class Destination
{
public string Name { get; set; }
public int SomeOtherValue { get; set; }
// Used only for mapping purposes
internal bool IsMapped { get; set; }
}
Try this,
Mapper.CreateMap<A, Destination>();
Mapper.CreateMap<Source, Destination>()
.ForMember(destination => destination.Name, options => options.MapFrom(source => Mapper.Map<A, Destination>(source.MyA).Name));
var objSource = new Source { SomeOtherValue = 7, MyA = new A { Id = 1, Name = "SomeName" } };
var result = Mapper.Map<Source, Destination>(objSource);

Mapping multiple collections to a single collection using Automapper

I am able to map group of sametype of collections to a single collection using the below code.
AutoMapper.Mapper.CreateMap<Source, Destination>().ForMember(
dest => dest.Drivers,
opt => opt.MapFrom(src => src.BikeDrivers.Concat(src.CarDrivers).Concat(src.TruckDrivers)));
With the above solution I am able to map all the three type of drivers into one collection.
My destination object (Driver) has a property called DriverType which helps in identifying the type of driver. (BikeDriver/CarDriver/TruckDriver)
In the above code, how i can set the DriverType property based on the collection I am adding.
for eg: i have to hard code
DriverType = CarDriver for CarDrivers collection items
DriverType = BikeDriverfor BikeDrivers collection item.
Thanks in advance
To set the DriverType property you have to have this knowledge in your source object. I can't see your big picture, but this maybe used as a sample
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var s = new Source()
{
BikeDrivers = new List<BikeDriver>() {new BikeDriver()},
CarDrivers = new List<CarDriver>() {new CarDriver()},
TruckDrivers = new List<TruckDriver>() {new TruckDriver()},
};
var d = new Destination();
AutoMapper.Mapper.CreateMap<Source, Destination>().ForMember(
dest => dest.Drivers,
opt => opt.MapFrom(src => src.BikeDrivers.Concat<IDriver>(src.CarDrivers).Concat<IDriver>(src.TruckDrivers)));
var result = AutoMapper.Mapper.Map(s, d);
}
public class Driver : IDriver
{
public string DriverType { get; set; }
}
public class Destination
{
public IEnumerable<IDriver> Drivers { get; set; }
}
public class Source
{
public IEnumerable<BikeDriver> BikeDrivers { get; set; }
public IEnumerable<CarDriver> CarDrivers { get; set; }
public IEnumerable<TruckDriver> TruckDrivers { get; set; }
}
public interface IDriver
{
string DriverType { get; set; }
}
public class BikeDriver : IDriver
{
public string DriverType
{
get { return "BikeDriver"; }
set { throw new NotImplementedException(); }
}
}
public class CarDriver : IDriver
{
public string DriverType
{
get { return "CarDriver"; }
set { throw new NotImplementedException(); }
}
}
public class TruckDriver : IDriver
{
public string DriverType
{
get { return "TruckDriver"; }
set { throw new NotImplementedException(); }
}
}
}
}

Resources