This is my class setup. How do i map only Invalid=false for DTOReportObservation AND DTOReportObservationLocation items?
reports = Mapper.Map<List<Report>, List<DTOReport>>(userReports);
public class DTOReport
{
public List<DTOReportObservation> Observations;
}
public class DTOReportObservation
{
public Guid ReportObservationID { get; set; }
public Guid ReportID { get; set; }
public bool Invalid { get; set; }
public List<DTOReportObservationLocation> ObservationLocations;
}
public class DTOReportObservationLocation
{
public Guid ReportObservationLocationID { get; set; }
public Guid ReportObservationID { get; set; }
public bool Invalid { get; set; }
}
CreateMap<Report, DTOReport>(MemberList.Source)
.ForMember(d => d.Observations, opt => opt.MapFrom(src => src.ReportObservations))
//??ReportObservations.Locations
With automapper you shouldn't need to create maps of lists. You just create a map from one type to another and let automapper iterate over the collections.
Can you also clarify what you mean by Invalid=false seeing as Invalid is a guid type.
For mapping only when invalid is false you can use the conditional mapping. https://automapper.readthedocs.io/en/latest/Conditional-mapping.html .
For more info on lists see here in the docs about collections. https://automapper.readthedocs.io/en/latest/Lists-and-arrays.html
Related
In Entity Framework Core version 2.2 or 3.0, is it possible to use owned/complex types in such a way that this kind of configuration is possible:
public class Product {
public int Id { get; set; }
public string Name { get; set; }
public ProductProperties Properties { get; set; }
}
public class ProductProperties {
public List<ProductSize> Sizes { get; set; }
}
public class Size {
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductSize {
public int ProductId { get; set; }
public Product Product { get; set; }
public int SizeId { get; set; }
public Size Size { get; set; }
}
modelBuilder.Entity<ProductSize>()
.HasOne(x => x.Product)
.WithMany(x => x.Properties.Sizes)
.HasForeignKey(x => x.ProductId);
modelBuilder.Entity<ProductSize>()
.HasOne(x => x.Size)
.WithMany()
.HasForeignKey(x => x.SizeId);
The error message which is seen for this kind of approach usually ends up in:
'x => x.Properties.Sizes' is not a valid property expression. The expression should represent a simple property access: 't => t.MyProperty'
An earlier found answer is almost exactly matching my question, but this was posted in 2013. By the time it was almost certainly not possible.
HasForeignKey relationship through a Complex Type property
The sources on Microsoft are only giving examples for creating an entity with the complex type itself, not for creating relationships between them.
The cause of the issue
In your sample code it's quite clear there is no specific Many to Many relation. To make my argument a bit more convincing what follows is a model of your entities and their relations:
The new class structure
For a Many to Many relation to work in EF the product and size tables need to have an implicit relation with each other through a singular junction table. In my proposed solution I've chosen the ProductProperty table. There I've added the fields from the productsize junction table:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<ProductProperty> Properties { get; set; }
}
public class ProductProperty
{
public int ProductId { get; set; }
public Product Product { get; set; }
public int SizeId { get; set; }
public Size Size { get; set; }
}
public class Size
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<ProductProperty> Properties { get; set; }
}
The functions
modelBuilder.Entity<ProductProperty>()
.HasKey(pp => new { pp.ProductId, pp.SizeId });
modelBuilder.Entity<ProductProperty>()
.HasOne(pp => pp.Product)
.WithMany(p => p.Properties)
.HasForeignKey(pp => pp.ProductId);
modelBuilder.Entity<ProductProperty>()
.HasOne(pp => pp.Size)
.WithMany(p => p.Properties)
.HasForeignKey(pp => pp.SizeId);
Additional advice (EDIT)
Make the "Size" class a generic property class. This way the Many-to-Many relation won't get broken and querying will also be very easy:
public class Property
{
public int Id { get; set; }
public PropertyType propType { get; set; }
public string propValue { get; set; }
}
public enum PropertyType
{
Size,
Fontsize,
...
}
As a final argument this change will make it easier to change existing properties or add new ones
Sources
https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration
You can check the owned entity types released in 2019 Check documentation here
An example from the link is the following:
public class Distributor
{
public int Id { get; set; }
public ICollection<StreetAddress> ShippingCenters { get; set; }
}
The owns many function should help you like this:
modelBuilder.Entity<Distributor>().OwnsMany(p => p.ShippingCenters, a =>
{
a.WithOwner().HasForeignKey("OwnerId");
a.Property<int>("Id");
a.HasKey("Id");
});
Let me know if I misunderstood your question.
I am having issue with Automapper when i have a collection.
Below is my situaion
Bussiness Model
public class Member
{
public int MemberId { get; set; }
public int TeamID { get; set; }
public Team Team { get; set; }
}
and i want to return below model from controller
public class Member
{
public int MemberId { get; set; }
public string Team { get; set; }
public int TeamId { get; set; }
}.
I have trying for something like below but not Team string remains null. I beleive i have to specify the mapping. But i am new to automapper i can't figure it out.
IEnumerable<Models.Member> ienumerableDest = _mapper.Map<IEnumerable<Entities.Member>, List<Models.Member>>(members);
Here is the Response
My guess is you haven't specified anywhere how to map your classes.
In your Entities.Member class you have a Team property of type Team, but you are trying to map it to string, so it fails.
In your configuration, add a profile which specifies this mapping, like this:
public class AppProfile : Profile
{
public AppProfile()
{
CreateMap<Entities.Member, Model.Member>(MemberList.Destination)
.ForMember(d => d.Team, opt => opt.MapFrom(src => src.Team.Name));
}
}
In this example i assumed a property "Name" in your Team object.
And in your startup class you should register your profile in your AutoMapper configuration.
I am trying to map from a child object of source to destination(as parent object).
Source Model:
public class SourceBaseResponse<T> where T : new()
{
public string Type { get; set; }
public string Id { get; set; }
public T Attributes { get; set; }
}
For my example I am using T to be of type SourceAssignment
public class SourceAssignment
{
public string Id { get; set; }
public string Email { get; set; }
public string EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTimeOffset CreatedAt { get; set; }
}
Destination Object
public class DestinationAssignment
{
public string Id { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
I want to map Source Model directly to Destination. So, I was trying to use
CreateMap<SourceAssignment, DestinationAssignment>();
CreateMap<SourceBaseResponse<SourceAssignment>, DestinationAssignment>()
.ForMember(dest => dest, opt => opt.MapFrom(src => AutoMapperConfig.Mapper.Map<DestinationAssignment>(src.Attributes)));
This is not working as I am getting run time error in the above line that "Custom configuration for members is only supported for top-level individual members on a type."
So, as per this thread I tried the following
CreateMap<SourceBaseResponse<SourceAssignment>, DestinationAssignment>()
.AfterMap((src, dst) => Mapper.Map(src.Attributes, dst));
Now, I am getting error where mapping should happen which says "Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance."
I am able to use ForMember for each property and map it from src.Attributes to dest(For eg: src.Attribute.Id to dest.Id). This works, but I do not really want to do this as my Source are complex classes involving nested childs(as this is a Web API response and I do not have control over this). So a lot of custom mapping is done here
CreateMap<SourceAssignment, DestinationAssignment>();
Any suggestions on how to proceed.
Resolution context is needed to be able to call Mapper.Map(), you can get resolution context by using ConstructUsing():
CreateMap<SourceChild, Destination>();
CreateMap<Source, Destination>()
.ConstructUsing((src, ctx) => ctx.Mapper.Map<Destination>(src.SourceChild));
I have 2 entities and everything works fine except NHibernate won't load the FieldGroupItems property in the second entity on an object.
I suspect it's because there is a circular dependency between the 2 entities.
I really need both ChildGroups and FieldGroupItems. If I remove ChildGroups than FieldGroupItems is loaded fine.
Is there a way to have what I want. The only way I can think of is to use Guid collections instead of object collections to store only the Ids and fetch data manually from code.
Any help is appreciated.
public class FieldGroupItemInstance : TenantBaseEntity
{
public virtual Guid ItemId { get; set; } //ID from the database to update actual object later
public virtual bool IsTemporaryId { get; set; } //true if field group is new (doesn't exist in system)
public virtual IList<QuestionnaireInstanceField> Fields { get; set; }
public virtual IList<QuestionnaireFieldGroupInstance> ChildGroups { get; set; }
public FieldGroupItemInstance()
{
Fields = new List<QuestionnaireInstanceField>();
ChildGroups = new List<QuestionnaireFieldGroupInstance>();
}
}
public class QuestionnaireFieldGroupInstance : TenantBaseEntity
{
public virtual Guid FieldGroupTemplateId { get; set; }
public virtual IList<FieldGroupItemInstance> FieldGroupItems { get; set; } //Each repeated group of instances
//public virtual FieldGroupItemInstance Parent { get; set; }
public QuestionnaireFieldGroupInstance()
{
FieldGroupItems = new List<FieldGroupItemInstance>();
}
}
If you are using fluentnhibernate to map your entities, this should work. Just pay attention to the Cascade options.
public class FieldGroupItemInstanceMap()
{
public FieldGroupItemInstanceMap()
{
Table("FieldGroupItemInstance");
HasManyToMany(x => x.ChildGroups)
.Table("FieldGroupItemInstance_QuestionnaireFieldGroupInstance")
.ParentKeyColumn("IdFieldGroupItemInstance")
.ChildKeyColumn("IdQuestionnaireFieldGroupInstance")
.Cascade.None();
}
}
public class QuestionnaireFieldGroupInstanceMap()
{
public QuestionnaireFieldGroupInstanceMap()
{
Table("QuestionnaireFieldGroupInstance");
HasManyToMany(x => x.FieldGroupItems)
.Table("FieldGroupItemInstance_QuestionnaireFieldGroupInstance")
.ParentKeyColumn("IdQuestionnaireFieldGroupInstance")
.ChildKeyColumn("IdFieldGroupItemInstance")
.Cascade.None();
}
}
I have the following scenario.
public class DictionaryEntity
{
public virtual string DictionaryName { get; set; }
public virtual IList<DictionaryRecordEntity> DictionaryRecord { get; set; }
}
public class DictionaryDto
{
public string DictionaryName { get; set; }
public IList<DictionaryRecordEntity> DictionaryRecord { get; set; }
}
I'm using Automapper and NHibernate. In NHibernate the DictionaryRecord property is marked as lazy loaded.
When I make the mapping from DictionaryEntity -> DictionaryDto, Automapper loads all my DictionaryRecords.
But I don't want this behavior, is there a way to configure the Automapper in order to don't resolve this property until I really access this property.
My workaround for this situation consists of splitting the DictionaryEntity in 2 classes and create a second Automapper mapping.
public class DictionaryDto
{
public string DictionaryName { get; set; }
}
public class DictionaryDtoFull : DictionaryDto
{
public IList<DictionaryRecordEntity> DictionaryRecord { get; set; }
}
and then in the code, depending on the need, call AutoMapper.Map appropriately.
return Mapper.Map<DictionaryDto>(dict);
return Mapper.Map<DictionaryDtoFull>(dict);
Does somebody have a better solution for my problem?
You must add a condition to validate if the collection is initialized to be mapped. You can read here more details: Automapper: Ignore on condition of.
AutoMapper.Mapper.CreateMap<DictionaryEntity, DictionaryDto>()
.ForMember(dest => dest.DictionaryRecord, opt => opt.PreCondition(source =>
NHibernateUtil.IsInitialized(source.DictionaryRecord)));
You could ignore the property, if this is of use?
AutoMapper.Mapper.CreateMap<DictionaryEntity, DictionaryDto>()
.ForMember(dest => dest.DictionaryRecord,
opts => opts.Ignore());
http://cpratt.co/using-automapper-mapping-instances/