Strange Automapper behavior for derived object mapping - c#

guys.
I have a strange behavior of Automapper when I try to map derived objects from one root.
For example:
public class User
{
public Guid Id { get; set; }
}
public class DerivedUser1 : User
{
public int SomeProp1 { get; set; }
}
public class DerivedUser2 : User
{
public int SomeProp2 { get; set; }
public class SubDerivedUser3 : DerivedUser2
{
public int SomeProp3 { get; set; }
}
I use CreateMap like this:
CreateMap<User, User>()
.ForMember(e => e.Id, f => f.Ignore())
.IncludeAllDerived()
.AfterMap((src, dest) =>
{
dest.Id = Guid.NewGuid();
});
Let see the code of mapping below:
var testUser1 = new DerivedUser1();
var testUser2 = new SubDerivedUser3();
mapper.Map(testUser2, testUser1, testUser2.GetType(), testUser1.GetType());
When I try to execute this code, I have an error:
"No coercion operator is defined between types 'DerivedUser1' and
'DerivedUser2'"
I thought that IncludeAllDerived() always check a whole derivation tree. Could someone explain to me the reason of this error?
Thank you.

Related

AutoMapper - Get error when trying to map two classes

I am trying to use AutoMapper to map a DTO to an Entity class but I keep getting an error.
Here is the DTO Class:
public class Product
{
public string ID { get; set; }
public string SKU { get; set; }
public string Name { get; set; }
public PriceTiers PriceTiers { get; set; }
}
and here is the Entity:
public partial class Product
{
public Product()
{
PriceTiers = new List<PriceTiers>();
}
[Key]
public string ID { get; set; }
public string SKU { get; set; }
public string Name { get; set; }
public virtual ICollection<PriceTiers> PriceTiers { get; set; }
}
Why do I keep getting the following error?
{"Missing type map configuration or unsupported
mapping.\r\n\r\nMapping types:\r\nPriceTiers ->
ICollection1\r\nWeb.Areas.DEAR.DTOs.PriceTiers -> System.Collections.Generic.ICollection1[[Web.Areas.DEAR.Data.PriceTiers,
Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]\r\n\r\n
Destination Member:\r\nPriceTiers\r\n"}
This is what I have in the Profile class:
AllowNullCollections = true;
CreateMap<DTOs.Product, Data.Product>();
CreateMap<DTOs.PriceTiers, Data.PriceTiers>();
and this is what I use to map the classes:
var products = _mapper.Map<IEnumerable<Product>>(result.Products);
This is what is in the Program.cs:
builder.Services.AddAutoMapper(typeof(AutoMapperProfiles).Assembly);
The exception message is quite clear, the AutoMapper doesn't know how to map the data from DTOs.PriceTiers to ICollection<Data.PriceTiers>.
Solution 1: Map from DTOs.PriceTiers to ICollection<Data.PriceTiers>
I believe that Custom Type Converters is what you need.
Create Custom Type Converters.
public class ICollectionDataPriceTiersTypeConverter : ITypeConverter<DTOs.PriceTiers, ICollection<Data.PriceTiers>>
{
public ICollection<Data.PriceTiers> Convert(DTOs.PriceTiers src, ICollection<Data.PriceTiers> dest, ResolutionContext context)
{
if (src == null)
return default;
var singleDest = context.Mapper.Map<Data.PriceTiers>(src);
return new List<Data.PriceTiers>
{
singleDest
};
}
}
Add to mapping profile.
CreateMap<DTOs.PriceTiers, ICollection<Data.PriceTiers>>()
.ConvertUsing<ICollectionDataPriceTiersTypeConverter>();
Demo # .NET Fiddle
Solution 2: Map from ICollection<DTOs.PriceTiers> to ICollection<Data.PriceTiers>
If the PriceTiers in DTOs.Product supports multiple items and mapping with many to many (to ICollection<Data.ProductTiers>), then consider modifying the property as the ICollection<DTOs.PriceTiers> type.
namespace DTOs
{
public class Product
{
...
public ICollection<PriceTiers> PriceTiers { get; set; }
}
}
Did you added "CreateMapper()" method after your configurations?
Try something like that.
public class MappingProfile : Profile
{
public MappingProfile {
AllowNullCollections = true;
CreateMap<DTOs.Product, Data.Product>();
CreateMap<DTOs.PriceTiers, Data.PriceTiers>();
}
}
After that, on your container service, inject this dependency:
var mappingConfig = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new MappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
builder.Services.AddSingleton(mapper);
After some more research I found out that my mapping profile was not in the right order. These are the changes I made.
public class AutoMapperProfiles : Profile
{
public AutoMapperProfiles()
{
AllowNullCollections = true;
CreateMap<DTOs.PriceTiers, Data.PriceTiers>();
CreateMap<DTOs.Product, Data.Product>()
.ForMember(dto => dto.PriceTiers, opt => opt.MapFrom(x => x.PriceTiers));
}
}
Now it maps perfectly

Using AutoMapper with EF Code First joins whilst sharing mappings

Given the following:
public class Foo
{
public Int32 Foo_PK { get; set; }
public String SomeProperty { get; set; }
}
public class Bar
{
public Int32 Bar_PK { get; set; }
public Int32 Foo_FK { get; set; }
public String SomeOtherProperty { get; set; }
}
public class JoinResult<TEntity, TJoiningEntity>
{
public TEntity From { get; private set; }
public TEntity To { get; private set; }
public JoinResult(TEntity from, TEntity to)
{
this.From = from;
this.To = to;
}
}
public interface IFooResult
{
public String SomeProperty { get; set; }
}
public interface IBarResult : IFooResult
{
public String SomeOtherProperty { get; set; }
}
public class FooResultDTO : IFooResult, IBarResult
{
public String SomeProperty { get; set; }
public String SomeOtherProperty { get; set; }
}
The idea behind this is that we some method of dispensing foo's and foo's with other related records, e.g. if there are 4 bar's then 4 rows in a table with the additional fields.
public class FooDispensary
{
public IQueryable<T> Dispense<T>()
where T: IFooResult
{
using (var repository = new Repository())
{
// TODO: Handle mapping for Foo -> FooResult
// Project to
return repository.Foos.ProjectTo<FooResultDTO>();
}
}
public IQueryable<T> DispenseWithBars<T>()
where T : IFooResult, IBarResult
{
using (var repository = new Repository())
{
// TODO: Handle mapping for JoinResult.From (same as Foo -> FooResult) as well as to JoinResult.To
// Project to
return repository.Foos.Join((f) => f.Foo_PK,
(b) => b.Foo_FK,
(f, b) => new JoinResult<Foo, Bar>(f, b))
.ProjectTo<FooResultDTO>();
}
}
}
However, I would ideally like to only specify the base mapping once (Foo -> IFooResult) and then re-use this in the methods where we need to join to a child table.
There are multiple reasons behind wanting to do this which are specific to my project however no need to go into them, I am just wondering if this is possible as I have struggled with the syntax thus far?
Thanks
Create a Map between Foo and FooResult. Because the Property SomeProperty is named the same in both the source and target Automapper will be able to figure out the mapping implicitly.
// TODO: Handle mapping for Foo -> FooResult
AutoMapper.Mapper.CreateMap<Foo, FooResult>();
Then create a map between JoinResult<Foo, Bar> and FooResultDTO
// TODO: Handle mapping for JoinResult.From (same as Foo -> FooResult) as well as to JoinResult.To
AutoMapper.Mapper.CreateMap<JoinResult<Foo, Bar>, FooResultDTO>()
.ForMember(r => r.SomeProperty, opt => opt.MapFrom(f => f.From.SomeProperty)
.ForMember(r => r.SomeOtherProperty, opt => opt.MapFrom(f => f.To.SomeOtherProperty)
However, I would ideally like to only specify the base mapping once (Foo -> IFooResult) and then re-use this in the methods where we need to join to a child table.
You're not resusing the mapping between Foo and IFooResult anywhere in your example. Your second function needs to map between JoinResult<Foo, Bar> and FooResultDTO as shown above. If you need to reuse mappings I suggest you look into using an AutoMapper Profile and managing a singleton AutoMapper instance that can be shared between your functions: https://github.com/AutoMapper/AutoMapper/wiki/Configuration

ravendb index across multiple nested properties

I am asking how to create an index based upon two different nested properties on an document. I am executing these queries through C#.
public class LocationCode
{
public string Code {get;set;}
public string SeqId {get;set;}
}
public class ColorCode
{
public string Code {get;set;}
public string SeqId {get;set;}
}
public class TestDocument
{
public int Id {get;set;}
public List<LocationCode> Locations { get; set; }
public List<ColorCode> Colors { get; set; }
}
I have experimented with various AbstractIndexCreationTask, Map, and Map+Reduce, but to no avail.
I would like to be able to do a query such as:
Get all documents where any Locations.Code property is "USA", AND/OR Colors.Code="RED", or on the SeqId property. I dont know whether this would mean I need multiple indexes. Normally I would either be comparing the Code property on both nested classes, or the Seq, but never mixed.
Please could someone point me in the right direction.
Many thanks
Phil
Create your index like this:
public class TestIndex : AbstractIndexCreationTask<TestDocument, TestIndex.IndexEntry>
{
public class IndexEntry
{
public IList<string> LocationCodes { get; set; }
public IList<string> ColorCodes { get; set; }
}
public TestIndex()
{
Map = testDocs =>
from testDoc in testDocs
select new
{
LocationCodes = testDoc.Locations.Select(x=> x.Code),
ColorCodes = testDoc.Colors.Select(x=> x.Code)
};
}
}
Then query it like this:
var q = session.Query<TestIndex.IndexEntry, TestIndex>()
.Where(x => x.LocationCodes.Any(y => y == "USA") &&
x.ColorCodes.Any(y => y == "RED"))
.OfType<TestDocument>();
Full unit test here.

Fluent NHibernate PropertyNotFoundException for Auto Property

I'm trying to get Fluent NHibernate to map a collection for me. My class definitions are as follows:
public abstract class Team
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
}
public class ClientTeam : Team
{
public virtual IEnumerable<Client> Clients { get; set; }
}
public class Client
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual string Identifiers { get; set; }
}
My mappings:
public class TeamMap : ClassMap<Team>
{
public TeamMap()
{
Table("Team");
Id(x => x.Id).GeneratedBy.Assigned();
Map(t => t.TeamName);
}
}
public class ClientTeamMap : SubclassMap<ClientTeam>
{
public ClientTeamMap()
{
HasMany(t => t.Clients);
}
}
public class ClientMap : ClassMap<Client>
{
public ClientMap()
{
Table("Client");
Id(c => c.Id);
Map(c => c.Name);
Map(c => c.Identifiers);
}
}
I've built a unit test that instantiates a team and then attempts to persist it (the test base has dependency configuration, etc. in it):
public class TeamMapTester : DataTestBase
{
[Test]
public void Should_persist_and_reload_team()
{
var team = new ClientTeamDetail
{
Id = Guid.NewGuid(),
TeamName = "Team Rocket",
Clients = new[]
{
new ClientDetail {ClientName = "Client1", ClientIdentifiers = "1,2,3"}
}
};
using (ISession session = GetSession())
{
session.SaveOrUpdate(team);
session.Flush();
}
AssertObjectWasPersisted(team);
}
}
When I run the test, I get this error:
SetUp : FluentNHibernate.Cfg.FluentConfigurationException : An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.
Database was not configured through Database method.
----> NHibernate.MappingException: Could not compile the mapping document: (XmlDocument)
----> NHibernate.PropertyNotFoundException : Could not find field '_clients' in class 'ClientTeam'`
I've looked through the NHibernate documentation and done some google searching, but I can't find anything that appears to address this issue. The documentation for Fluent NHibernate's Referencing methods explicitly uses auto properties, so I'm sure that's not the issue.
Why might NHibernate think that _clients is the field it should map in this case?
And the reason turns out to be: Conventions.
The Fluent mappings were set up to try to enforce read-only collection properties, by requiring a backing field. The ICollectionConvention in question:
public class CollectionAccessConvention : ICollectionConvention
{
public void Apply(ICollectionInstance instance)
{
instance.Fetch.Join();
instance.Not.LazyLoad();
instance.Access.CamelCaseField(CamelCasePrefix.Underscore);
}
}
which requires that collection backing fields be camelCased and start with an underscore.

Nhibernate linq fetch in subclass

Help me, please, solve one problem.
I have project, which uses Nhibernate and Fluent Nhibernate. There I created one base class
(it is not real classes, but they describe my situation):
public class Document
{
public virtual int Id { get; private set; }
public virtual Account Acc { get; private set; }
}
And mapping for it:
public class DocumentMap: ProfileEntityMap<Document>
{
public DocumentMap()
{
Id(m => m.Id);
References(m => m.Acc);
DiscriminateSubClassesOnColumn("Type");
}
}
Then I implemented subclass:
public class PaymentDocument: Document
{
public virtual Card AccountCard { get; set;}
}
Mapping for class PaymentDocument:
public class PaymentDocumentMap : SubclassMap<PaymentDocument>
{
public PaymentDocumentMap()
{
References(t => t.AccountCard);
}
}
And after that I try execute this query:
payments = session.Query<PaymentDocument>()
.Fetch(t => t.Acc)
.Fetch(t => t.AccountCard)
.ToList();
And when I insert first fetch I get next exception:
Object reference not set to an instance of an object.
Can somebody answer me where is a problem?
Actually it was a bug fixed in 3.0.0.Alpha2. Right now it works with the trunk.

Categories