Using .NET Core with EF and Automapper. How can I map the Property of a List Item to a List of same type as the Property?
I'd like to map the int JobId from each Job in Address.JobHistory to the List<int> JobHistory of my AddressDTO
Address Class
public class Address {
public int AddressId { get; set; }
...other properties...
public List<Job> JobHistory { get; set; }
}
AddressDTO Class
public class AddressDTO {
public int AddressId { get; set; }
...other properties...
public List<int> JobHistory { get; set; }
}
Job Class
public class Job {
public int JobId { get; set; }
...other properties...
public Address JobAddress { get; set; }
}
AddressController Automapper Config (this is where I need to make the magic happen, but below is what I have now)
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Address, AddressDTO>()
...map other properties...
.ForMember(dto => dto.JobHistory, opt => opt.MapFrom(addr => addr.JobHistory));
Looks like I'll need to use a Custom Type Converter and supply my own method to do the conversion.
Related
I have model as below
class MyClass()
{
public int Id { get; set; }
public List<Item> Items { get; set; }
}
class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
both are added to DBContext as DbSets, now I would like to filter out the MyClass using the value of the Name property in the Items collection. How do I do this?
First of all correct your POCOs this way:
public class MyClass
{
public int Id { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public virtual MyClass MyClass {get;set}
public int MyClassId {get;set}
}
Usage:
Presented query will return all MyClass instances, where at least one item's Name will satisfy condition:
var answer = db.MyClass.Where(c => c.Items.Any(item => item.Name == "Sam")).ToList();
This query will return all MyClass instances, where all item's Name will satisfy condition:
var answer = db.MyClass.Where(c => c.Items.All(item => item.Name == "Sam")).ToList();
I have this model:
public class RepairRequest
{
[Key]
public int Id { get; set; }
public List<RepairAction> RepairActions { get; set; }
public decimal TotalPrice => RepairActions.Sum(r => r.ActionPrice);
public string LastOperation => RepairActions.LastOrDefault().RepairOperation.Description;
}
public class RepairAction
{
[Key]
public int Id { get; set; }
public int RepairRequestId { get; set; }
public RepairRequest RepairRequest { get; set; }
public int RepairOperationId { get; set; }
public RepairOperation RepairOperation { get; set; }
public decimal ActionPrice { get; set; }
}
public class RepairOperation
{
[Key]
public int Id { get; set; }
public string Description { get; set; }
}
I'm trying to query RepairRequests and get TotalPrice and also LastOperation in a List but doesn't work for both properties. This is what I have tried till now:
using (var context = new ServiceManagerContext(new DbContextOptions<ServiceManagerContext>())) {
var data = context.RepairRequests
.Include(r => r.RepairActions).ThenInclude(r => r.RepairOperation); // Only LastAction works
//.Include("RepairActions").Include("RepairActions.RepairOperation"); // Only LastAction works
//.Include(r => r.RepairActions); // Only TotalPrice works
//.Include("RepairActions"); // Only TotalPrice works
var repairRequest = data.FirstOrDefault(r => r.Id == 5);
Assert.NotNull(repairRequest);
Assert.Equal(60.0m, repairRequest.RepairPrice);
Assert.Equal("Παραδόθηκε", repairRequest.LastAction);
}
Thank you.
I'd consider avoiding attempting to resolve calculated properties in your domain entities and instead look to resolve those when querying the data to populate view models.
If your view model needs the TotalPrice and LastOperation, then provided a Repository or such returning IQueryable you can expand the query to return what is needed using deferred execution rather than attempting to rely on eager loading the entire tree:
I.e.
IQueryable<RepairRequest> requests = context.RepairRequests.Where(x => x.Id == 5); // Or pull from a Repository returning the IQueryable
var viewModelData = requests.Select(x => new {x.Id, TotalPrice = x.RepairActions.Sum(), LastOperation = x.RepairActions.LastOrDefault()?.RepairOperation?.Description }).SingleOrDefault();
This should execute a more optimized query and return you an anonymous type with just the data you need to populate whatever view model you want to display. The iffy bit is around situations where there are no repair actions, or a repair action without an operation.. EF should avoid the null ref and just return null. the ?. syntax may not be necessary or supported, so it may just need to be ".". Using a method where you eager or lazy load those related entities and execute Linq off the entity instances, be careful around .SingleOrDefault() and drilling down into child fields.
Firstaball you have to declare Foreign Keys, and flag virtual properties like :
public class RepairRequest
{
[Key]
public int Id { get; set; }
public virtual ICollection<RepairAction> RepairActions { get; set; }
public decimal TotalPrice => RepairActions.Sum(r => r.ActionPrice);
public string LastOperation => RepairActions.LastOrDefault().RepairOperation.Description;
}
public class RepairAction
{
[Key]
public int Id { get; set; }
public decimal ActionPrice { get; set; }
public int RepairRequestId { get; set; }
[ForeignKey("RepairRequestId ")]
public virtual RepairRequest RepairRequest { get; set; }
public int RepairOperationId { get; set; }
[ForeignKey("RepairOperationId")]
public RepairOperation RepairOperation { get; set; }
}
Then you could call this, which load all children values :
var data = context.RepairRequests.Include("RepairActions.RepairOperation");
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
I have the following models
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Child> Child { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<GrandChild> GrandChild { get; set; }
}
public class GrandChild
{
public int Id { get; set; }
public string Name { get; set; }
}
What Im trying to do now is select a list of parents from the database with the children and grandchildren.
Tried the following with no joy:
List<Parent> parent = new List<Parent>();
parent = db.parent.ToList();
Use the Include method:
parent = db.parent.Include(parent => parent.Child.Select(child => child.GrandChild)).ToList();
For older versions of Entity Framework, you have to use a string instead of a lambda expression:
parent = db.parent.Include("Child.GrandChild").ToList();
Or you can use the custom Include extension method I blogged about here.
in Linq to Sql you should use DataLoadOptions
var dlo = new DataLoadOptions();
dlo.LoadWith<Parent>(p => p.Child );
dlo.LoadWith<Child>(c=> c.GrandChild );
db.LoadOptions = dlo;
List<Parent> parent = new List<Parent>();
parent = db.parent.ToList();
I have this entity:
public class DynamicPage {
public int PageId { get; set; }
public int Order { get; set; }
public string MenuText { get; set; }
public string MenuHover { get; set; }
public int? ParentId { get; set; }
public virtual DynamicPage Parent { get; set; }
public virtual ICollection<DynamicPage> Children { get; set; }
}
This entity may have 3 level: Parent -> Child -> Grandchild. How can I load the Parent (level 1) whit all associated children (level 2) and for each child, associated grandchild (level 3) if any? Thanks to help.
EF 4.1 feature and syntax:
var entity = context.Parents
.Include(p => p.Children.Select(c => c.GrandChildren))
.FirstOrDefault(p => p.Id == 1); // or whatever condition
If you want to make life easy on yourself, follow the EF Code First conventions of naming your table IDs simply Id (or, alternatively, name of table + Id, e.g., DyanmicPageId).
This should leave you with something like this:
public class DynamicPage
{
public int Id { get; set; }
public int Order { get; set; }
public string MenuText { get; set; }
public string MenuHover { get; set; }
public int? ParentId { get; set; }
public virtual DynamicPage Parent { get; set; }
public virtual ICollection<DynamicPage> Children { get; set; }
}
Then you need to set up the relationship between parents and children explicitly in an OnModelCreating method in your DbContext class.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<DynamicPage>()
.HasMany(page => page.Children)
.WithRequired(child => child.Parent)
.HasForeignKey(child => child.ParentId);
}
You can then select children or grandchildren as needed:
var parent = dbContext.DynamicPages.Where(page => page.ParentId == null);
var children = parent.Children;
var grandchildren = parent.SelectMany(page => page.Children);
var allRelatedPages = parent.Union(children).Union(grandchildren);