Automapper: mapping collection to single object via projection - entity-framework

I have a set of classes.
public class Student
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ScoreRecord> ScoreRecords { get; set; }
}
public class ScoreRecord
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int StudentId { get; set; }
public virtual Student Student { get; set; }
public string Subject { get; set; }
public int Score { get; set; }
}
public class ScoreModel
{
public int MinScore { get; set; }
public int MaxScore { get; set; }
}
public class StudentViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public ScoreModel Score { get; set; }
}
I need to perform this mapping:
Mapper.CreateMap<Student, StudentViewModel>()
.ForMember(d => d.Score, opts => opts.MapFrom(m => m.ScoreRecords));
Mapper.CreateMap<ICollection<ScoreRecord>, ScoreModel>()
.ForMember(d => d.MinScore, opts => opts.MapFrom(m => m.Min(s => s.Score)))
.ForMember(d => d.MaxScore, opts => opts.MapFrom(m => m.Max(s => s.Score)));
The following code causes an exception:
var student = context.Students.ProjectTo<StudentViewModel>().FirstOrDefault(e => e.Id == 1);
Exception info:
An unhandled exception of type 'System.NotSupportedException' occurred
in EntityFramework.SqlServer.dll
Additional information: Cannot compare elements of type 'System.Collections.Generic.ICollection`1[[EFTest.Entities.ScoreRecord,
EFTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'. Only
primitive types, enumeration types and entity types are supported.
If I do not use the projection, then the mapping works correctly. The following code is executed without errors.
var student = ctx.Students.FirstOrDefault(e => e.Id == 1);
var studentViewModel = Mapper.Map<StudentViewModel>(student);
Why mapping with projection is failing and how to fix it?
Automapper version: 4.0.4,` EntityFramework version: 6.1

It's because of a null check that happens when you have a map for a property. Check the execution plan.
You can avoid it by setting AllowNullDestinationValues to false (globally or per profile). Or you can upgrade and set AllowNull per member.

Related

How can I order by many-to-many extra column in Entity Framework Core?

I am using Entity Framework Core 3. Member and Request entities are related many-to-many. But I have an extra column named Order. The member request order is saved in the MemberRequest table.
public class Member
{
public int MemberID { get; set; }
public string Name { get; set; }
public virtual ICollection<MemberRequest> MemberRequests { get; set; }
}
public class Request
{
public int RequestID { get; set; }
public string Message { get; set; }
public virtual ICollection<MemberRequest> MemberRequests { get; set; }
}
public class MemberRequest
{
[Key, Column(Order = 0)]
public int MemberID { get; set; }
[Key, Column(Order = 1)]
public int RequestID { get; set; }
public virtual Member Member { get; set; }
public virtual Request Request { get; set; }
public int Order { get; set; }
}
I want to get two select using Entity Framework Core:
public IEnumerable<Member> Get(int id)
{
var members = await _context.Request
.Include(e => e.MemberRequests)
.Where(e => e.MemberRequests.Any(m => m.RequestID == id))
.ToListAsync();
// How can I order by MemberRequest.Order ???
return member;
}
But I could not order result by MemberRequest.Order in MemberRequest entity. How can I do it?

Entity Framework 6 Weak Entity mapping expects a key to be present on add migration

in our domain we have entity aggregate which is consist of ParkingLot :
public class ParkingLot : EntityBase
{
public string Title { get; set; }
public int EntryNumber { get; set; }
public int Floor { get; set; }
public int Capacity { get; set; }
public int FilledCapacity { get; set; }
public int? BuildingId { get; set; }
public int ParkingSlotId { get; set; }
public virtual ICollection<ParkingLotGate> ParkingLotGates { get; set; }
}
And then ParkingLotGate which is a weak entity that does not deserve to have an independent Id:
public class ParkingLotGate
{
public int ParkingLotIdentity { get; set; }
public int GateNumber { get; set; }
public int UserId { get; set; }
public virtual ParkingLot ParkingLot { get; set; }
public virtual User User { get; set; }
}
It is desired that the ParkingLotIdentity , GateNumber , UserId to be present as the PK of this weak entity in the database model.The mapping between the two is as
public ParkingLotGateMap()
{
this.HasRequired(plg => plg.ParkingLot)
.WithMany(pl => pl.ParkingLotGates)
.HasForeignKey(plg => plg.ParkingLotIdentity);
this.HasRequired(plg => plg.User)
.WithMany(pl => pl.ParkingLotGates)
.HasForeignKey(plg => plg.UserId);
this.HasKey(plg => new {
plg.UserId,plg.ParkingLotIdentity,
plg.GateNumber });
}
When I want to add migration it produces the error like this :
EntityType 'ParkingLotGate' has no key defined. Define the key for this
EntityType.
ParkingLotGates: EntityType: EntitySet 'ParkingLotGates' is based on type
'ParkingLotGate' that has no keys defined.
How would I EF make understand that the dependent POCO is not an entity and has a composite key!?
Any help apperciated.

Referenced object is not loaded from database

This the table structure I have:
#region Tables
public class WorkoutProfile
{
public WorkoutProfile()
{
WorkoutExercises = new List<WorkoutExercise>();
}
[Key]
public int ProfileId { get; set; }
public string Name { get; set; }
public int Sets { get; set; }
public int RestAfterSetInSeconds { get; set; }
public virtual User User { get; set; }
public virtual ICollection<WorkoutExercise> WorkoutExercises { get; set; }
}
public class WorkoutExercise
{
[Key]
public int WorkoutId { get; set; }
public virtual Exercise Exercise { get; set; }
public int Order { get; set; }
public int WorkoutTimeInSeconds { get; set; }
public int RestAfterInSeconds { get; set; }
}
public class Exercise
{
[Key]
public long ExerciseId { get; set; }
public string Title { get; set; }
public string Visualisation { get; set; }
public bool IsDefault { get; set; } // Is exersice should be included when user first registers
}
public class User
{
[Key]
public long UserId { get; set; }
public string Email { get; set; }
public DateTime Registered { get; set; }
}
#endregion Tables
In the repository class I run the following linq query:
return context
.WorkoutProfiles.Include(w => w.WorkoutExercises)
.Where(q => q.User.UserId == userId && q.ProfileId == profileId)
.FirstOrDefault();
and I receive the good and old "Object reference not set to an instance of an object". When examining the result, see that Exercises property in WorkoutExercises is null.
This is how the database is created using code first approach:
So, the question is: why Exercises not included in WorkoutExercises object? Do I need to include it somehow? I am using .NET Core 2
The simple answer would be no lazy loading in EFCore. Not Released yet but if you want to dabble with alpha code, its in the repository. Based on your classes there are no collections for exercises in WorkoutExcercise.
Then you need to ThenInclude(w => w.Exercises) following your Include clause since EFCore doesn't do lazy loading.
I found a solution following this post
Altered my code as following:
var top = context
.Set<WorkoutProfile>()
.Where(q => q.ProfileId == profileId && q.User.UserId == userId)
.Include(q => q.WorkoutExercises)
.SingleOrDefault();
context
.Entry(top)
.Collection(e => e.WorkoutExercises)
.Query()
.OfType<WorkoutExercise>()
.Include(e => e.Exercise)
.Load();
And it worked

How to filter child collections Entity Framework

WEB API
Model::
public class Empresa
{
[Key]
public string CDEmpresa { get; set; }
public string NomeFantasia { get; set; }
[IgnoreDataMember]
public string Nome{ get; set; }
public List<EmpresaRamoAtividade> EmpresaRamoAtividade { get; set; }
}
public class EmpresaRamoAtividade
{
[Key]
public int CTRamoAtividade { get; set; }
[IgnoreDataMember]
public string CDEmpresa { get; set; }
public List<RamoAtividade> RamoAtividade { get; set; }
}
public class RamoAtividade
{
[IgnoreDataMember]
[Key]
public int CTRamoAtividadeTraducao { get; set; }
public int CTRamoAtividade { get; set; }
public string Atividade { get; set; }
public int Idioma { get; set; }
}
Controller::
Working ok:::
{
return db.Empresas
.Where(a => a.Associado.IsAssociado)
.Include(empresaRamo => empresaRamo.EmpresaRamoAtividade)
.Include(ramo => ramo.EmpresaRamoAtividade.Select(atividade => atividade.RamoAtividade));
}
Not working, I have to filter by "idioma" (language):::
{
return db.Empresas
.Where(a => a.Associado.IsAssociado)
.Include(empresaRamo => empresaRamo.EmpresaRamoAtividade)
.Include(ramo => ramo.EmpresaRamoAtividade.Select(atividade => atividade.RamoAtividade.Where(idioma => idioma.Idioma == 1)));
}
Error:
The Include path expression must refer to a navigation property
defined on the type. Use dotted paths for reference navigation
properties and the Select operator for collection navigation
properties. Parameter name: path
Can't I filter 3 level collection child?
Thank you..
You cannot filter using Include methods. It only supports select.
Disclaimer: I'm the owner of the project Entity Framework Plus (EF+)
EF+ Query IncludeFilter allow you to easily filter related entities:
{
return db.Empresas
.Where(a => a.Associado.IsAssociado)
.IncludeFilter(empresaRamo => empresaRamo.EmpresaRamoAtividade)
.IncludeFilter(ramo => ramo.EmpresaRamoAtividade.Select(atividade => atividade.RamoAtividade.Where(idioma => idioma.Idioma == 1)));
}
You can find the documentation here

Mapping Odd relationship in Entity Framework 6

I have a set of 3 models, which is an odd many-to-many-ish relationship.
public class Metric {
public int Id { get; set; }
public string Name { get; set; }
// ...
}
public class ActionPlan {
public int Id { get; set; }
public string Name { get; set; }
public DateTime StartDate { get; set; }
//...
public virtual ICollection<Metric> Metrics { get; set; }
}
public class PlanMetric {
public int PlanId { get; set; }
public int MetricId { get; set; }
public decimal GoalValue { get; set; }
public virtual ActionPlan Plan { get; set; }
public virtual Metric Metric { get; set; }
}
I have the relationships mapped as follows:
public class PlanMetricMapping : EntityTypeConfiguration<PlanMetric> {
public PlanMetricMapping() {
ToTable("PlanMetric");
HasKey(m => new {
m.MetricId,
m.PlanId
});
Property(m => m.GoalValue)
.IsRequired()
.HasPrecision(10, 2);
HasRequired(m => m.Metric)
.WithMany()
.HasForeignKey(m => m.MetricId);
HasRequired(m => m.Plan)
.WithMany()
.HasForeignKey(m => m.PlanId);
}
}
public class ActionPlanMapping : EntityTypeConfiguration<ActionPlan> {
public ActionPlanMapping() {
ToTable("ActionPlan");
HasKey(m => m.Id);
// ...
//HasMany(m=>m.Metrics) // how do I get to this data?
}
}
The problem is
1) EF is creating an ActionPlan_Id field in my Metric table, and I'm not sure why.
2) I don't know how to set up my mapping to be able to navigation from a Plan to it's Metrics.
EF is creating an ActionPlan_Id field because you have
public virtual ICollection<Metric> Metrics { get; set; }
in your ActionPlan definition, which EF interprets as a one-to-many relationship between ActionPlan and Metric. It seems like you want
public virtual ICollection<PlanMetric> PlanMetrics { get; set; }
instead.
Then, in order to get to an ActionPlan's metrics, you could go through that collection, perhaps through a Select().