How can I create a Map with Automapper when in the underlying destination type a property not yet has initialized?
Example:
public class UserAccount
{
public string name { get; set; }
public Dictionary<string,string> properties { get; set; }
}
public class UserAccountOtherType
{
public string name { get; set; }
public string Property1 {get;set; }
}
public static UserAccount CustomMap(UserAccountOtherType type2)
{
AutoMapper.Mapper.CreateMap<UserAccount,UserAccountOtherType>()
.ForMember(dest => dest.properties["Property1", opt => opt.MapFrom(src => (string)src.Property1));
return AutoMapper.Mapper.Map<UserAccount,UserAccountOtherType>(type2);
}
When I try to execute this code it fails because the Dictionary in UserAccount is not yet initialized. I cannot initialize the Object by myself because the UserAccount Class is a Datacontract of a WCF Serviceinterface.
I have to create a Dicationary by myself and assign it to the property.
UserAccount b = new UserAccount();
Dictionary<string,string> properties = new Dictionary<string,string>();
b.properties = properties;
How can I solve this with Automapper? Or is my approach not senseful?
Only way I can think of to do something like this is to write a class that implements AutoMapper.IValueResolver. Then opt.MapFrom... becomes opt.ResolveUsing....FromMember... Probably at that point your destination is the entire dictionary (I think if you leave out the FromMember you get the whole object into your resolver
in your implementation of IValueResolver.Resolve, try breaking into the debugger and checking out the ResolutionContext in source.Context, then once you have your dictionary built, return source.New(myDictionary)
Related
I am using automapper to map some objects between the database and another representation.
The entity looks something like
public class MyEntity {
public int Id { get; set; }
public Guid RowId { get; set; }
}
public class MyObject {
public Guid Id { get; set; }
}
As you can see, the names and types are unaligned.
Since I got many Entities and Objects, I'd rather not CreateMap<A, B>().ForMember(d => d.Id, mex => mex.MapFrom(s => s.RowId));.
To not having to do the above Convention:
AddMemberConfiguration()
.AddMember<NameSplitMember>()
.AddName<ReplaceName>(_ => _.AddReplace("RowId", "Id"));
This does not what I suspected it to do and I was not able to figure out, how to use the ReplaceName Convention.
So I'd like to hear ideas about how to map that types.
MyEntity and MyObject both are base types, so I could also use that.
What I'm trying to archieve in pseudo-code:
if(source is MyEntity && target is MyObject)
{
target.Id = source.RowId;
}
ForAllMembers
On recommendation of #lucian-bargaoanu I tried looking into ForAllMembers.
I did the following in the MapperProfile:
public class MapperProfile : Profile {
public MapperProfile() {
ForAllMaps(MapEntityBaseId);
}
protected void MapEntityBaseId(TypeMap map, IMappingExpression mex)
{
if (!map.SourceType.IsSubclassOf(typeof(EntityBase)))
return;
if (!map.DestinationType.IsSubclassOf(typeof(MyObject)))
return;
mex.ForMember("Id", opt => opt.MapFrom("RowId"));
}
}
also the debugger hints me, that ForAllMember is executed as expected, it still fails the mapping.
I created a GIST for the ForAllMembers: https://gist.github.com/anonymous/511a1b69b795aa2bc7e7cd261fcb98b1
We are working with AutoMapper 4.1.1 (will soon update to 6, but we need some time to change the project) and have this issue: in the source class we have a property "int? MyProp" while in the destination we have string "MyPropValue", which should not be mapped from MyProp. AutoMapper however does map it when the value of MyProp is not null (using MyProp.Value, flattened). We could use Ignore() to ignore the mapping but as we have dozens of such properties, we are looking for a way to do it with one configuration, without breaking the rest of the mapping.
Here is some example code:
class Program
{
static void Main(string[] args)
{
var map = Mapper.CreateMap<Dto, Model>();
var dto = new Dto() {MyProp = 10};
var model = Mapper.Map<Dto, Model>(dto);
Console.WriteLine(model.MyPropValue); // MyPropValue should be empty but it gets mapped
}
}
public class Dto
{
public int? MyProp { get; set; }
}
public class Model
{
public string MyPropValue { get; set; }
}
Mapper.CreateMap<SourceType, DestinationType>()
.ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull));
Allows you to skip all nulls.
Automapper skip null values with custom resolver
This is a reach, but I am going to ask anyway.
I'll lead with my example:
public class PatientInfoModel : IPatientInfoModel, IHaveCustomMappings
{
public string PatientId { get; set; }
public string PatientIdForView { get; set; }
public PatientEpisodeData PatientEpisode { get; set; }
public void CreateMappings(Profile configuration)
{
configuration.CreateMap<PatientInfoRawDto, PatientInfoModel>()
.ForMember(m => m.PatientIdForView, opt => opt.ResolveUsing<PatientIdResolver<PatientInfoRawDto, PatientInfoModel>>())
.ForMember(m => m.PatientId, opt => opt.MapFrom(p => p.patID))
.ForMember(m => m.PatientEpisode, opt => opt.MapFrom(p => new PatientEpisodeData
{
PatientId = p.patID,
PatientIdForView = this.PatientIdForView
}));
}
public class PatientEpisodeData
{
public int PatientId { get; set; }
public string PatientIdForView { get; set; }
}
}
As you can see, with the member PatientEpisode, I would like to map from one of the properties which has already been resolved (PatientIdForView).
As I could not figure out how to do this, I just set the property after the fact. But it would be interesting to find out if this is possible.
Note: I'm not really interested in using a custom value resolver unless you could pass the PatientIdForView property to it.
Cheers
Custom value resolvers do allow you to pass in the destination member value into it (I assume that's what the PatientIdForView property you mention is, the destination member value). If you need the source member value, you can use a member value resolver:
http://docs.automapper.org/en/stable/Custom-value-resolvers.html
You get the destination member, the source member that you specify, and the source/destination objects. Should be everything you need!
I'm trying to map 2 classes inheriting different base (but with common property). When I use Map, parent properties doesn't map automatically (which I think should based on inhertiance laws). Please suggest if I'm wrong somewhere:
public class SourceBase
{
public bool IsSuccess { get; set; }
}
public class DestBase
{
public bool Success { get; set; }
}
public class ChildSource : SourceBase
{
public string SourceName { get; set; }
}
public class ChildDest : DestBase
{
public string DestName { get; set; }
}
Creating Maps
AutoMapper.Mapper.CreateMap<SourceBase, DestBase>()
.ForMember(dest => dest.Success, opt => opt.MapFrom(source => source.IsSuccess));
AutoMapper.Mapper.CreateMap<ChildSource, ChildDest>()
.ForMember(dest => dest.DestName,opt=>opt.MapFrom(source=>source.SourceName));
Using the Map
ChildSource ch = new ChildSource()
{
IsSuccess = true,
SourceName = "user1"
};
var obj = AutoMapper.Mapper.Map<ChildDest>(ch);
I expected IsSuccess as True and DestName as user1. But only SourceName gets set and IsSuccess remains false. If I use same name (IsSuccess) in both, it works which is because of automapping via name. But How can I use the existing format of different property names (but same types) in different class. I do not want to explicitly map parent properties while writing map for each child class.
You need to tell AutoMapper about the inheritance by using the Include method:
Mapper.CreateMap<SourceBase, DestBase>()
.Include<ChildSource, ChildDest>()
.ForMember(dest => dest.Success, opt => opt.MapFrom(source => source.IsSuccess));
Here is a sample of what I am trying to accomplish:
public class BaseClass<T>
{
public static T GetByID(int ID)
{
// Need database name here that is determined at design time in the derived class.
var databaseName = "";
// do some stuff involving database name that gets me object by ID here.
return default(T);
}
}
public class DerivedClass : BaseClass<DerivedClass>
{
private string DatabaseName { get; set; }
}
Basically, how would I access the derived "DatabaseName" in the base class static GetByID method?
EDIT: After I posted this, I tried one more thing. I played with attributes earlier, and failed, but I think my brain was mushy. Just tried again and ran a test, and it is working. Here is the updated sample.
public class BaseClass<T>
{
public static T GetByID(int ID)
{
// Need database name here that is determined at design time in the derived class.
var databaseName = ((DatabaseAttribute)typeof(T).GetCustomAttributes(typeof(DatabaseAttribute), true).First()).DatabaseName;
// do some stuff involving database name that gets me object by ID here.
return default(T);
}
}
[Database("MyDatabase")]
public class DerivedClass : BaseClass<DerivedClass>
{
}
public class DatabaseAttribute : Attribute
{
public DatabaseAttribute(string databaseName)
{
DatabaseName = databaseName;
}
public string DatabaseName { get; set; }
}
Base class to derived class is a one-way inheritance: The base class has no knowledge of the existance of a derived class, and so it can't access it.
In addition to that you will have a hard time accessing a non-static property from a static method.
I know you've already answered your own question, but some improvements....
Add a where clause to guarantee inheritance, it means any static methods can make use of inherited methods. You might also want to add the new() clause if you wish to be able to create instances of the inherited class.
public class BaseClass<T> : where T : BaseClass<T>
{
static readonly string databaseName;
static BaseClass() {
// Setup database name once per type of T by putting the initialization in
// the static constructor
databaseName = typeof(T).GetCustomAttributes(typeof(DatabaseAttribute),true)
.OfType<DatabaseAttribute>()
.Select(x => x.Name)
.FirstOrDefault();
}
public static T GetByID(int ID)
{
// Database name will be in the static field databaseName, which is unique
// to each type of T
// do some stuff involving database name that gets me object by ID here.
return default(T);
}
}
[Database("MyDatabase")]
public class DerivedClass : BaseClass<DerivedClass>
{
}
public class DatabaseAttribute : Attribute
{
public DatabaseAttribute(string databaseName)
{
DatabaseName = databaseName;
}
public string DatabaseName { get; set; }
}