Make AutoMapper automatically map prefixed properties - c#

I want AutoMapper to map automatically Members like this:
class Model
{
public int ModelId { get; set; }
}
class ModelDto
{
public int Id { get; set; }
}
Here, I would do a
CreateMap<Model, ModelDTO>()
.ForMember(x => x.Id, e => e.MapFrom(x => x.ModelId)
But, how could I make AutoMapper do the mapping automatically? Most of my classes are like that. The Primary key is in the form: ClassName + "Id".
Edit
I've tried with this, but it doesn't work:
class Program
{
static void Main(string[] args)
{
Mapper.Initialize(exp =>
{
exp.CreateMap<User, UserDto>();
exp.ForAllPropertyMaps(map => map.DestinationProperty.Name.Equals("Id"), (map, expression) => expression.MapFrom(map.SourceType.Name + "Id"));
});
var user = new User() { UserId = 34};
var dto = Mapper.Map<UserDto>(user);
}
}
public class UserDto
{
public int Id { get; set; }
}
class User
{
public int UserId { get; set; }
}

Yes, the code looks reasonable, but it doesn't work. That's because it runs after the property maps are computed. And there are none in this case, because the names don't match. My bad :) Try
exp.ForAllMaps((typeMap, mappingExpression) =>
mappingExpression.ForMember("Id", o=>o.MapFrom(typeMap.SourceType.Name + "Id"))
);

Related

AutoMapper Mapping a collection of strings to a property of a collection inside another collection

How to mapping IdContributors (collection of strings) to a collection (Contributors), inside a collection TAction, with a property (ContributorId) of string, using LINQ and AutoMapper ?
public ViewModelToDomainMappingProfile()
{
CreateMap<ActionViewModel, TAction>();
//.ForMember(d => d.Contributors, opt => opt.MapFrom(a => ids = a.IdContributors.Select(x => { })));
}
Models
Model TAction
public class TAction
{
public Guid Id {get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<TActionContributor> Contributors { get; set; }
}
public class TActionContributor
{
public Guid TActionId { get; set; }
[ForeignKey("TActionId")]
public TAction Action { get; set; }
public string ContributorId { get; set; }
[ForeignKey("ContributorId")]
public ApplicationUser Contributor { get; set; }
}
ActionViewModel
public class ActionViewModel
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<string> IdContributors { get; set; }
}
I can see two choices here, you'll choose the solution which better suits your needs:
1) Select() in from MapFrom()
CreateMap<ActionViewModel, TAction>()
.ForMember(
destination => destination.Contributors,
options => options.MapFrom((source, destination) =>
{
var contributors = source
.IdContributors
.Select(id => new TActionContributor
{
Action = destination,
Contributor = new ApplicationUser
{
Id = id,
},
ContributorId = id,
TActionId = source.Id,
})
.ToList();
contributors.ForEach(ac => ac.Contributor.Contributors = contributors);
return contributors;
}));
2) Custom ValueResolver
This is the same as the first solution, maybe more readable if you want to keep your mapping profiles clean and move the custom logic away when possible.
public class TActionContributorValueResolver : IValueResolver<ActionViewModel, TAction, ICollection<TActionContributor>>
{
public ICollection<TActionContributor> Resolve(
ActionViewModel source,
TAction destination,
ICollection<TActionContributor> destMember,
ResolutionContext context)
{
var contributors = source
.IdContributors
.Select(id => new TActionContributor
{
Action = destination,
Contributor = new ApplicationUser
{
Id = id,
},
ContributorId = id,
TActionId = source.Id,
})
.ToList();
contributors.ForEach(ac => ac.Contributor.Contributors = contributors);
return contributors;
}
}
Configuration:
CreateMap<ActionViewModel, TAction>()
.ForMember(
destination => destination.Contributors,
options => options.MapFrom<TActionContributorValueResolver>());
Final note:
contributors.ForEach(ac => ac.Contributor.Contributors = contributors);
Line above allows you to traverse endlessly from TAction through TActionContributor to ApplicationUser and the other way. If you don't need that feature, feel from to remove it and return the contributors list right away.

Automapper suddenly creates nested object

Entities:
public class Entity
{
public int Id { get; set; }
}
public class User : Entity
{
public string Name { get; set; }
public Company Company { get; set; }
}
public class Company : Entity
{
public string Name { get; set; }
}
Dto's:
public class EntityDto
{
public int Id { get; set; }
}
public class UserDto : EntityDto
{
public string Name { get; set; }
public int? CompanyId { get; set; }
}
So I want to map User to UserDto like User.Company == null => UserDto.CompanyId == null and vice versa.
That is my Automapper configuration:
Mapper.Initialize(configuration =>
{
configuration
.CreateMap<User, UserDto>()
.ReverseMap();
});
This works fine:
[Fact]
public void UnattachedUserMapTest()
{
// Arrange
var user = new User { Company = null };
// Act
var userDto = Mapper.Map<User, UserDto>(user);
// Assert
userDto.CompanyId.Should().BeNull();
}
but this test fails:
[Fact]
public void UnattachedUserDtoMapTest()
{
// Arrange
var userDto = new UserDto { CompanyId = null };
// Act
var user = Mapper.Map<UserDto, User>(userDto);
// Assert
user.Company.Should().BeNull();
}
Details:
Expected object to be <null>, but found
Company
{
Id = 0
Name = <null>
}
Doesn't work for me:
...
.ReverseMap()
.ForMember(user => user.Company, opt => opt.Condition(dto => dto.CompanyId != null));
and well as that (just for example):
...
.ReverseMap()
.ForMember(user => user.Company, opt => opt.Ignore());
Why does Automapper create nested object and how can I prevent it?
That "suddenly" bit is funny :)
configuration.CreateMap<User, UserDto>().ReverseMap().ForPath(c=>c.Company.Id, o=>o.Ignore());
You have a default MapFrom with CompanyId and that is applied in reverse. For details see this and a few other similar issues.
In the next version (on MyGet at the moment) you'll also be able to use
configuration.CreateMap<User, UserDto>().ReverseMap().ForMember(c=>c.Company, o=>o.Ignore());

Fluent NHibernate: ISet of base class

In my project I have a base class (not mapped):
public abstract class BaseEntity
{
public virtual string Name { get; set; }
public virtual string Description { get; set; }
}
Also I have a few inherited classes (they look all almost the same, so here is a code and map for only one)
public class User : BaseEntity
{
public virtual int UserId { get; set; }
public virtual string Login { get; set; }
public virtual string PasswordHash { get; set; }
public virtual ISet<BaseEntity> Entities { get; set; }
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
this.Id(x => x.UserId);
this.Map(x => x.Login);
this.Map(x => x.PasswordHash);
this.HasManyToMany<BaseEntity>(x => x.Entities);
}
}
Next, I have a NHibernateHelper:
public class NHibernateHelper
{
public static ISession OpenSession()
{
ISessionFactory sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(#"someconstring")
.ShowSql()
)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<User>())
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(false, true))
.BuildSessionFactory();
return sessionFactory.OpenSession();
}
}
And here is a question:
How can I exclude BaseEntity class from mapping, if I need table like EnitiyToEntity in my Database for many-to-many relationship?
Take a look to this:
https://www.codeproject.com/Articles/232034/Inheritance-mapping-strategies-in-Fluent-Nhibernat
If I understand your question the solution should be to implement TPC (Table per concrete class).
By the way, in your mapping you have to use the concrete type for HasManyToMany.
For example (I supposed your user is referenced to many groups):
HasManyToMany<Group>(x => x.Entities).Table("UsersGroups");
where the Group class is something like this:
public class Group : BaseEntity
{
public virtual int GroupId { get; set; }
public virtual string PasswordHash { get; set; }
public virtual ISet<BaseEntity> Members { get; set; }
}
And in the GroupMap class you can reference the users like this:
HasManyToMany<User>(x => x.Members).Table("UsersGroups");
If you reference a class you have to map it. So map Entity as ClassMap and all the others as SubclassMap. They will end up as union subclass which is one table per class. Unfortunatly you can not map a hasmanytoany with FNH.
You can map it as hasmanytomany and work around it:
var config = new Configuration();
config.BeforeBindMapping += BeforeBindMapping;
_config = Fluently
.Configure(config)
...
private void BeforeBindMapping(object sender, NHCfg.BindMappingEventArgs e)
{
var userclass = e.Mapping.RootClasses.FirstOrDefault(rc => rc.name.StartsWith(typeof(User).FullName));
if (userclass != null)
{
HbmSet prop = (HbmSet)paymentclass.Properties.FirstOrDefault(rc => rc.Name == "Entities");
prop.Item = new HbmManyToAny // == prop.ElementRelationship
{
column = new[]
{
new HbmColumn { name = "entityType", notnull = true, notnullSpecified = true },
new HbmColumn { name = "entity_id", notnull = true, notnullSpecified = true }
},
idtype = "Int64",
metatype = "String",
metavalue = typeof(Entity).Assembly.GetTypes()
.Where(t => !t.IsInterface && !t.IsAbstract && typeof(Entity).IsAssignableFrom(t))
.Select(t => new HbmMetaValue { #class = t.AssemblyQualifiedName, value = t.Name })
.ToArray()
};
}
}

LINQ - append MemberBinding expression into exist MemberInit expression

Basic idea is similar to Merging Expression Trees to Reuse in Linq Queries.
In my situation, I have two models and DTOs:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public Extra Extra { get; set; }
}
public class Extra
{
public int Id { get; set; }
public string Text { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; }
public ExtraDto Extra { get; set; }
}
public class ExtraDto
{
public int Id { get; set; }
public string Text { get; set; }
}
and expressions:
Expression<Func<Extra, ExtraDto>> extraSelector = o => new ExtraDto
{
Id = o.Id,
Text = o.Text
};
Expression<Func<User, UserDto>> userSelector = o => new UserDto
{
Id = o.Id,
Name = o.Name
};
Now, I'd like to 'append' extraSelector into userSelector. The pseudo code is like:
var selectorExpression = userSelector.Append(user => user.Extra, extraSelector);
Context.Users.Select(selectorExpression).ToList();
The final expression would be like this:
Expression<Func<User, UserDto>> userSelector = o => new UserDto
{
Id = o.Id,
Name = o.Name,
Extra = new ExtraDto
{
Id = o.Extra.Id,
Text = o.Extra.Text
}
};
I've tried using ExpressionVisitor, but no luck.
Apart from the "merge" of the two selectors, you have to insert the "path" o => o.Extra into the extraSelector and create a new "bind expression" for the property Extra of UserDto.
In fact, i'm playing around with such scenarios within this project, where i've tried to abstract this kind of expression plumbing. Your "merge" would then look like that:
userSelector = extraSelector.Translate()
.Cross<User>(o => o.Extra)
.Apply(o => o.Extra, userSelector);
The Translate extension method is just a little helper to make use of type inference, Cross inserts o => o.Extra into the extraSelector, Apply creates the "bind expression" for the property Extra of UserDto and finally merges the result with userSelector.

Mapping "LinkedList" with AutoMapper

I have linked list kind of situation. My DTO looks like this -
public class DTOItem
{
public string ID
{
get;
set;
}
public int? UniqueId
{
get;
set;
}
public string Payload
{
get;
set;
}
//How do I map this guy? It is list of same type.
public List<DTOItem> RelatedItems
{
get;
set;
}
}
How do I map this guy using AutoMapper? I am able to map other members of the class. Data is mapped from another class' collection object that has a different set of member not identical to this class.
public List<DTOItem> RelatedItems
{
get;
set;
}
Thanks in advance.
UPDATE: Here is the code -
Raphael, here is the code:
The Source Objects:
public class ResultsSet
{
public int? ResultId
{
get;
set;
}
public int UID
{
get;
set;
}
//Returns large XML string
public string ResultBlob
{
get;
set;
}
public RelatedItems[] RelatedSet
{
get;
set;
}
}
public class RelatedItems
{
public int Item_ID
{
get;
set;
}
public int Relationship_ID
{
get;
set;
}
public string Description
{
get;
set;
}
}
To map here is the code:
Mapper.CreateMap<ResultSet, DTOItem>()
.ForMember(dest => dest.ID, opt => opt.MapFrom(src => src.ResultID.GetValueOrDefault(0)))
.ForMember(dest => dest.UniqueId, opt => opt.MapFrom(src => src.UID))
.ForMember(dest => dest.Payload, opt => opt.MapFrom(src => src.ResultBlob));
/*
How do I map RelatedSet to RelatedItems here?
*/
Mapper.Map(result, returnResult);
Thanks again.
No need to use AutoMapper for this.
For non-cyclic, relatively flat data, this should do:
static Func<RelatedItems, DTOItem> MapRelated(IEnumerable<ResultsSet> all) {
var map = MapResultSet(all);
return relatedItem => map(all.First(x => x.UID == relatedItem.Item_ID));
}
static Func<ResultsSet, DTOItem> MapResultSet(IEnumerable<ResultsSet> all) {
return s =>
new DTOItem {
ID = s.ResultId.GetOrElse(0).ToString(),
UniqueId = s.UID,
Payload = s.ResultBlob,
RelatedItems = (s.RelatedSet ?? new RelatedItems[0]).Select(MapRelated(all)).ToList()
};
}
Sample usage:
var data = new[] {
new ResultsSet {
UID = 1,
RelatedSet = new[] {
new RelatedItems { Item_ID = 2 },
new RelatedItems { Item_ID = 3 },
},
},
new ResultsSet {
UID = 2,
},
new ResultsSet {
UID = 3,
},
};
var items = data.Select(MapResultSet(data)).ToList();
Debug.Assert(items.Count == 3);
Debug.Assert(items[0].UniqueId == 1);
Debug.Assert(items[1].UniqueId == 2);
Debug.Assert(items[2].UniqueId == 3);
Debug.Assert(items[0].RelatedItems.Count == 2);
Debug.Assert(items[0].RelatedItems[0].UniqueId == items[1].UniqueId);
Debug.Assert(items[0].RelatedItems[1].UniqueId == items[2].UniqueId);
I assumed Item_ID is the 'key' to UID, otherwise simply adjust MapRelated.
Generally speaking, I think AutoMapper is only useful if you have to map untyped data into typed data, and even in that case I'd think really hard before using it. Otherwise, some LINQ code is simpler and more type safe.

Categories