AutoMapper populate other properties based on another one - c#

I have this scenario
public class ObjectViewModel
{
public int Isn { get; set; }
public int? APO_Isn { get; set; }
public string Name { get; set; }
}
public class ObjectModel
{
public int Isn { get; set; }
public int? APO_Isn { get; set; }
public int? CLI_Isn { get; set; }
public int? EMP_Isn { get; set; }
}
//Configuration of AutoMapper: Mapper.CreateMap<ObjectViewModel, ObjectModel>().ReverseMap();
//On my controller: var objectModel= Mapper.Map<ObjectModel>(objectViewModel);
Suppose Isn property of objectViewModel equals 53. When I map the objectModel based on objectViewModel, for some reason unknown to me, the AutoMapper are entering the value 53 in Isn (That's ok), but are entering in CLI_Isn and EMP_Isn too and my ViewModel doesn't have those properties.
I did some test, changed CLI_Isn to Cli_Isn and EMP_Isn to Emp_Isn and the problem was solved. But I still do not understand what was causing the problem, that is why 53 is been moved to CLI_Isn and EMP_Isn.

What is happening is that you are triggering AutoMapper's Flattening feature
From that documentation:
When you configure a source/destination type pair in AutoMapper, the configurator attempts to match properties and methods on the source type to properties on the destination type. If for any property on the destination type a property, method, or a method prefixed with "Get" does not exist on the source type, AutoMapper splits the destination member name into individual words (by PascalCase conventions).
Since issue 402 was fixed this appears to be working with underscores as well, which is what you're seeing here.
There is seemingly no way to disable flattening, so you would have to explicitly exclude those properties:
Mapper.CreateMap<ObjectViewModel, ObjectModel>()
.ForMember(dest => dest.CLI_Isn, src => src.Ignore())
.ForMember(dest => dest.EMP_Isn, src => src.Ignore())
.ReverseMap();

Is it possible your Mapper.Initialize includes a call to RecognizeDestinationPrefixes("CLI_") or RecognizePrefixes("CLI_")? This would make automapper ignore "CLI_" when mapping types but not ignore "Cli_".

Related

AutoMapper How to select Collection's property, project it into DTO & return projected collection?

Source models:
public class ExampleModel
{
public int Id { get; set; }
public float Something { get; set; }
public string Info { get; set; } = "";
}
public class ExampleModelContainer
{
public int Id { get; set; }
public ExampleModel Model { get; set; } = null!;
}
public class LargeEntity
{
public int Id { get; set; }
public List<ExampleModelContainer> ModelEntries { get; set; } = null!;
}
Target models:
public class ExampleModelDto
{
public float Something { get; set; }
public string Info { get; set; } = "";
}
public class LargeEntityDto
{
public int Id { get; set; }
public List<ExampleModelDto> Models { get; set; } = null!;
}
AutoMapper profile:
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<ExampleModel, ExampleModelDto>();
// One of my attempt, I thought converting container into model, and automatically example model into DTO. Failed
CreateMap<ExampleModelContainer, ExampleModel>()
.ConvertUsing(x => x.Model);
CreateMap<LargeEntity, LargeEntityDto>()
.ForMember(x => x.Models,
opt => opt.MapFrom(y => y.ModelEntries));
}
}
Expected result:
LargeEntities' List<ExampleModelContainer> collection is mapped into List<ExampleModelDto>.
Actual result:
Unhandled exception. AutoMapper.AutoMapperConfigurationException: The following member on AutoMapperExps.Models.LargeEntityDto cannot be mapped:
Models
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type AutoMapperExps.Models.LargeEntityDto.
Context:
Mapping to member Models from AutoMapperExps.Models.LargeEntity to AutoMapperExps.Models.LargeEntityDto
Exception of type 'AutoMapper.AutoMapperConfigurationException' was thrown.
at AutoMapper.Configuration.ConfigurationValidator.AssertConfigurationIsValid(IEnumerable`1 typeMaps)
at AutoMapper.Configuration.ConfigurationValidator.AssertConfigurationExpressionIsValid(IEnumerable`1 typeMaps)
at AutoMapper.MapperConfiguration.AssertConfigurationIsValid()
at Program.Main(String[] args) in D:\Experiments\AutoMapperExps\Program.cs:line 1
How I see it possible to be made:
Mapper takes source List<ExampleModelContainer> collection
Mapper Selects Model property from each element of collection
Mapper projects ExampleModel (returned from property above) into ExampleModelDto
Mapper returns List<ExampleModelDto> as a final result.
Note: This is made for Entity Framework queries. I want to do most projection operations on server-side.
TLDR: AutoMapper throws the exception because it cannot find a mapping ExampleModelContainer -> ExampleDto. If you define a mapping the error is fixed, e.g.:
CreateMap<ExampleModelContainer, ExampleModelDto>()
.IncludeMembers(x => x.Model);
The mapping tries to take several steps at once. If I understand your sample right, the configured mappings are like this:
ExampleModel -> ExampleDto
ExampleModelContainer -> ExampleModel
LargeEntity -> LargeEntityDto
When mapping the Models property in for LargeEntityDto, you are trying to map a List<ExampleModelContainer> without further configuration to a List<ExampleModelDto>. It may seem clear for AutoMapper to take the route
ExampleModelContainer -> ExampleModel -> ExampleDto
in this case, because there is only one path. But what if there were several paths to get from a ExampleModelContainer to a ExampleDto? The library would not be able to make a decision that solves all possible combinations.
This means that you as a developer have to make the decision by configuring a mapping from ExampleModelContainer to ExampleModelDto, e.g.:
CreateMap<ExampleModelContainer, ExampleModelDto>()
.IncludeMembers(x => x.Model);
This way, AutoMapper has a direct mapping between the types and is able to map the properties. See this fiddle to test.

EF Core - navigational property in index

I have the following two classes
public class Tip
{
public string Home { get; set; }
public string Away { get; set; }
public string Prediction { get; set; }
public Tipster Tipster { get; set; }
... other properties
}
public class Tipster
{
public int Id { get; set; }
public string Username { get; set; }
public string Platform { get; set; }
}
Now, I want to make unique index in theTip table. According to the EF Core documentation, there is no Data Annotations syntax, so I am using the fluent one:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Tip>()
.HasIndex(entity => new { entity.Tipster, entity.Home, entity.Away, entity.Prediction })
.HasName("IX_UniqueTip")
.IsUnique();
}
Now, when I update the database I get the following error
C:..>dotnet ef database update System.InvalidOperationException:
Cannot call Property for the property 'Tipster' on entity type 'Tip'
because it is configured as a navigation property. Property can only
be used to configure scalar properties.
It seems that EF didn't liked the fact that I am using referential property in the index. How can I fix that ?
You can't use navigation property in index defining expression. Instead, you should use the corresponding FK property.
The problem in your case is that you don't have explicit FK property in your model Tip. By convention EF Core will create int? TipsterId shadow property. So theoretically you should be able to use EF.Property method to access it:
.HasIndex(e => new { TipsterId = EF.Property<int>(e, "TipsterId"), e.Home, e.Away, e.Prediction })
Unfortunately this doesn't work currently (EF Core 2.0.1). So you have to resort to HasIndex overload with params string[] propertyNames:
.HasIndex("TipsterId", nameof(Tip.Home), nameof(Tip.Away), nameof(Tip.Prediction))
You must define the property TipsterId explicitly cause the Navigation property define it as shadow, so you cannot use it on custom index or alternate key
public class Tip
{
public string Home { get; set; }
public string Away { get; set; }
public string Prediction { get; set; }
public int TipsterId { get; set; }
public Tipster Tipster { get; set; }
... other properties
}
Now you can
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Tip>()
.HasIndex(entity => new { entity.TipsterId, entity.Home, entity.Away, entity.Prediction })
.HasName("IX_UniqueTip")
.IsUnique();
}
They way you defined your entities EF will put the referential column into the tipster table, since it looks like a 1-n relationship. Meaning a tipster can place several tips, but each tip is only placed by a single tipster.
That means on the database level there is nothing to index. No column, no key - nothing.
To fix this you might ask yourself what you really want to achieve with an index in the first place. An index is supposed to make queries using the columns of the index faster and avoid a full table scan.

ExpressMapper / EntityFramework - No parameterless constructor defined for this object

I am trying to use ExpressMapper to map data entities to models.
If I map entity to a model directly (both of them having same properties) then it is working fine.
But if I map linked entities to model then I am getting an error
There was an error: System.MissingMethodException: No
parameterless constructor defined for this object.
Database structure:
ExpressMapper Registration:
Mapper.Register<DiscountDaysOfWeek, DiscountDaysOfWeekModel>()
.Member(dest => dest.DiscountDayId, src => src.DiscountDayId)
.Member(dest => dest.DiscountDaysOfWeekId, src => src.DiscountDaysOfWeekId)
.Member(dest => dest.DiscountId, src => src.DiscountId)
.Member(dest => dest.Discountday, src => src.DiscountDay.Day);
Invoked like this:
var disDays = discs.SelectMany(x => x.DiscountDaysOfWeeks)
.Map<IQueryable<DiscountDaysOfWeek>, IQueryable<DiscountDaysOfWeekModel>>();
Getting the error message at the invoke.
DiscountDaysOfWeekModel:
public class DiscountDaysOfWeekModel
{
public int DiscountDaysOfWeekId { get; set; }
public int DiscountId { get; set; }
public int DiscountDayId { get; set; }
public string Discountday { get; set; }
}
DiscountDayOfWeek (Generated by EF)
public partial class DiscountDaysOfWeek
{
public int DiscountDaysOfWeekId { get; set; }
public int DiscountId { get; set; }
public int DiscountDayId { get; set; }
public virtual DiscountDay DiscountDay { get; set; }
public virtual Discount Discount { get; set; }
}
DiscountDay(Generated by EF):
public partial class DiscountDay
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public DiscountDay()
{
this.DiscountDaysOfWeeks = new HashSet<DiscountDaysOfWeek>();
}
public int DiscountDayId { get; set; }
public string Day { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<DiscountDaysOfWeek> DiscountDaysOfWeeks { get; set; }
}
Sample working one: In the below working sample the model and entities are having same properties
Mapper.Register<DiscountPreventedPriceEnding, DiscountPreventedPriceEndingModel>();
var execPriceEndings = discs.SelectMany(x => x.DiscountPreventedPriceEndings)
.Map<IQueryable<DiscountPreventedPriceEnding>, IQueryable<DiscountPreventedPriceEndingModel>>();
Any help would be greatly appreciated.
I appreciate this is an extremely old question, but given that I just spent 4 hours debugging a similar There was an error: System.MissingMethodException: No parameterless constructor defined for this object error on ExpressMapper, I thought I'd chime in with my findings.
So we had a situation similar to yours, in that we had domain models like so (all the following is simplified examples):
public class Owner
{
public int? ID { get; set; }
public string Name { get; set; }
}
public class Animal
{
public int? ID { get; set; }
public string Name { get; set; }
public int? OwnerID { get; set; }
[ForeignKey("OwnerID")]
public Owner Owner { get; set; }
}
With the following view model (i.e. what our APIs send out and receive):
public class AnimalViewModel
{
public int? ID { get; set; }
public string Name { get; set; }
public string Owner { get; set; }
}
With mappings like so:
Mapper.Register<Animal, AnimalViewModel>();
Mapper.Register<AnimalViewModel, Animal>();
On mapping to or from the domain model and the view model we'd get the MissingMethodException, despite the fact that both the view model and the domain model had public, default constructors. The solution was to manually map the related entities in the domain model and exclude them from ExpressMapper's mappings like so:
Mapper.Register<Animal, AnimalViewModel>()
.Ignore(a => a.Owner);
Mapper.Register<AnimalViewModel, Animal>()
.Ignore(a => a.Owner);
From reading EntityMapper's source code, it seems the MissingMethodException is a total red herring which has nothing to do with the actual issue. The actual issue seems to be that it can't figure out how to convert one type to another. In our case -- where complex objects were mapped to/from primitives as above -- it was sufficient to exclude the related objects from the mapper and do it manually.
EDIT:
Upon further investigation, we traced the root problem in our case back to the fact that EF proxy creation creates generated types (e.g. 'MyModel_14289012') which don't match the types registered in the mapper. To prevent this, apply the following to your context:
Context.Configuration.LazyLoadingEnabled = false;
Context.Configuration.ProxyCreationEnabled = false;
and manually include any nested/related objects required in your model like so:
Context.Animals
.Include(a => a.Owner);
This fetches the related entities, but as their actual type rather than the EF-generated type.
Entity Framework uses a parameterless constructor to instantiate classes and reflection to populate class properties. If you have constructors with parameters, then the default parameterless constructor is hidden and you have to add it to your Entity classes for Entity Framework to use.
But if I map linked entities to model then I am getting an error
If your child entities are missing the parameterless constructor and lazy loaded, then EF is failing when it attempts to instantiate the child entity which doesn't have a parameterless constructor.
Note: the parameterless constructor doesn't have to be public.

NHibernate - Fetch referenced column value

I have this NHibernate model:
public class RootTable
{
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual DateTime? Start { get; set; }
public virtual DateTime? Finish { get; set; }
public virtual IList<Leaf1> ChildCollection1 { get; set; }
}
public class Leaf1
{
public virtual int ID { get; set; }
public virtual string Info1 { get; set; }
public virtual string Info2 { get; set; }
public virtual RootTable Parent { get; set; }
}
public class RootMapping : ClassMap<RootTable>
{
public RootMapping()
{
Table("RootTable");
Id(c => c.Name);
Map(c => c.Description, "Desc").Length(20);
Map(c => c.Start).Length(20);
Map(c => c.Finish).Length(20);
HasMany(c => c.ChildCollection1)
.Cascade.All()
.LazyLoad()
.Inverse();
}
}
public class Leaf1Mapping : ClassMap<Leaf1>
{
public Leaf1Mapping()
{
Table("LeafTable1");
Id(c => c.ID, "RowID").GeneratedBy.Identity();
Map(c => c.Info1).Length(20);
Map(c => c.Info2).Length(20);
References(c => c.Parent).Column("Parent").LazyLoad();
}
}
What I'm trying to do is access the value of the referenced column in Leaf1 without lazyloading RootTable.
In otherwords, I have this:
this.LogMessage("Loading leaves...");
var allleafs = session.CreateCriteria(typeof(Leaf1)).List<Leaf1>();
this.LogMessage("Loaded leaves...");
var leaf = allleafs[0];
this.LogMessage("Leaf metadata:");
// This causes a lazy-load of the RootTable object.
this.LogMessage("Leaf parent is " + leaf.Parent);
Now what I actually want is the value of "Parent" as it's stored in the underlying database - I don't care for the parent object, I don't want to load it, all I want to do get the raw value. I can't access the field that contains the value (i.e., leaf.Parent.Name) as I want this to work in a generic fashion...
[Background]
Ultimately this is plugging into an auditing framework that uses an NHibernate interceptor, so this needs to work in a generic way so that for any object passed in I can report on the changed values. It's entirely possible the child node will have changed with no change to the root node, so when the interceptor's OnFlushDirty() is called, I do not want the interceptor to cause a lazy-load of other objects.
I know I can reference the parent property directly (e.g., I can say "leaf.Parent.Name") and this will get me the value without the lazy load, but there doesn't seem to be a quick way to determine that "Name" is the key property I want to return.
[Edited to add...]
Walking the tree doesn't seem to work as I get a null reference exception:
var theType = leaf.Parent.GetType();
// This line returns a NULL due to the proxy class.
var metadata = factory.GetClassMetadata(theType);
var idProp = metadata.IdentifierPropertyName;
var prop = theType.GetProperty(idProp);
var val = prop.GetValue(leaf.Parent, null);
this.LogMessage("Leaf parent is " + val);
Now, theType comes back as RootTableProxy, so is just a placeholder because the main class isn't loaded. Which means metadata is null as there is no class metadata and thus idProp fails with a null reference exception.
So I can't actually see how to get referenced column value without a lazy load somewhere along the way: surely this can't be right?
Edited to add (more!)
I thought an easy solution have been found by using session.GetIdentifier(). However this doesn't seem to work in all cases: in an interceptor calling session.GetIdentifier(state[i]) on some objects caused an exception stating that the object wasn't part of the current session, so still looking for a more reliable solution that doesn't resort to reflection. Any ideas welcome...
What about adding another property to your Leaf1 Class.
e.g.
public class Leaf1
{
public virtual int ID { get; set; }
public virtual string Info1 { get; set; }
public virtual string Info2 { get; set; }
public virtual RootTable Parent { get; set; }
public virtual int ParentId { get; set; }
}
and then map is as readonly
public class Leaf1Mapping : ClassMap<Leaf1>
{
public Leaf1Mapping()
{
Table("LeafTable1");
Id(c => c.ID, "RowID").GeneratedBy.Identity();
Map(c => c.Info1).Length(20);
Map(c => c.Info2).Length(20);
References(c => c.Parent).Column("Parent").LazyLoad();
Map(c => c.ParentId).Column("Parent").ReadOnly();
}
}
You could make your entities implement interfaces (would be cleaner), but if you want to get the actual type of the proxy, use NHibernateUtil.GetClass(proxy) which will return the underlying type that you're looking for
It transpires that ISession has a method GetIdentifier which returns the cached value of a property. Taking the example from my original post, the code changes to this:
this.LogMessage("Loading leaves...");
var allleafs = session.CreateCriteria(typeof(Leaf1)).List<Leaf1>();
this.LogMessage("Loaded leaves...");
var leaf = allleafs[0];
this.LogMessage("Leaf metadata:");
this.LogMessage("Leaf parent is " + session.GetIdentifier(leaf.Parent));
Now in testing this seems to be working nicely, and can be called from OnFlushDirty within an interceptor on both currentState and previousState objects.
I've been trying to find more information on GetIdentifier, but the NH documentation is sadly lacking, and there seems to be few, if any, blog posts that mention this. If anyone is aware of any caveats, problems, or "funnies" that I need to know aobut in relation to this method, I'd be more than happy to hear about them...
[Edited to add]
...as it turns out, this doesn't work if the object isn't a proxy object(!). Still looking for a reliable method to do this that doesn't involve reflection or decorating objects with attributes to identify fields.

Many-to-many relationships using EF Code First

I have two classes defined as such:
public class Questionnaire
{
public int QuestionnaireID { get; set; }
public string Title { get; set; }
public bool Active { get; set; }
public virtual ICollection<Question> Questions { get; set; }
public virtual ICollection<Vendor> Vendors { get; set; }
}
public class Vendor
{
public int VendorID { get; set; }
public string VendorName { get; set; }
public virtual ICollection<Questionnaire> OpenQuestionnaires { get; set; }
public virtual ICollection<Questionnaire> SubmittedQuestionnaires { get; set; }
public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
}
I beleive this is the correct way to establish a many-to-many relationship between these classes, and when the project is built, I would expect three tables to be created.
However, when I attempt to to relate one Questionnaire to two different Vendors, I receive the following error when attempting to save the changes (context.SaveChanges()):
*Multiplicity constraint violated. The role 'Vendor_OpenQuestionnaires_Source' of the relationship 'QuestionnaireApp.Models.Vendor_OpenQuestionnaires' has multiplicity 1 or 0..1.*
If I assign a Questionnaire to only one Vendor, save the changes and then assign it to another and again save changes I no longer get the error; however the Questionaire is then related only to the last Vendor to which it was assigned, indicating that (at best) there is a one-to-many relationship being created.
I'm hoping that there is something wrong with the way I'm declaring the many-to-many relationship between these classes, or perhaps there is something I need to add to the context class to "encourage" the relationsip, but perhaps many-to-many relationships like this are not supported, or cannot be created using "Code First"?
Thank you for your time,
Jason
If you don't have any Fluent API code your expected mapping relies on EF Code First conventions. The convention which you expect to kick in here is the AssociationInverseDiscoveryConvention. Now if you look in Intellisense (and probably also documentation) it says about this convention:
Convention to detect navigation properties to be inverses of each
other when only one pair of navigation properties exists between the
related types.
Now, that's the problem: You don't have only "one pair" of navigation properties between Questionnaire and Vendor. You have two collections in Vendor refering to Questionnaire and one collection in Questionnaire refering to Vendor. The result is that this convention doesn't get applied and EF maps actually three one-to-many relationships with only one end exposed as navigation property in the model.
Moreover the mapping you want to achieve is not possible with your model: You cannot map the one end Questionnaire.Vendors to the two ends Vendor.OpenQuestionnaires and Vendor.SubmittedQuestionnaires.
One workaround is to change your model the following way:
public class Vendor
{
public int VendorID { get; set; }
public string VendorName { get; set; }
[NotMapped]
public IEnumerable<Questionnaire> OpenQuestionnaires
{
get { return Questionnaires.Where(q => q.IsActive); }
}
[NotMapped]
public IEnumerable<Questionnaire> SubmittedQuestionnaires
{
get { return Questionnaires.Where(q => !q.IsActive); }
}
public virtual ICollection<Questionnaire> Questionnaires { get; set; }
public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
}
Now Vendor.Questionnaires is mapped to Questionnaire.Vendors (AssociationInverseDiscoveryConvention should detect this) and the helper properties OpenQuestionnaires and SubmittedQuestionnaires allow you to pull out the selected items. (I'm not sure if IsActive is your distinguishing flag. Otherwise you have to introduce some new flag.)
The [NotMapped] attribute is just here to make it explicite. It is probably not necessary because EF won't map IEnumerable collections and readonly properties with only a getter anyway.
Go figure, after an hour or so of searching, I go and find the exact answer 30 seconds after I post my question.
The solution was to add the following to the context class:
modelBuilder.Entity<Vendor>()
.HasMany<Questionnaire>(x => x.OpenQuestionnaires)
.WithMany(x => x.Vendors)
.Map(x =>
{
x.MapLeftKey("vID");
x.MapRightKey("qID");
x.ToTable("VendorQuestionnaires");
});
I found the answer by reading this Stack Overflow post: EF Code First Many-to-Many not working

Categories

Resources