Nested objects to one object using Automapper - c#

Have a requirement to map name (Class A) and phone number (Class B) to Class C which has both Name and PhoneNumber. A person (name) can have more than one phone numbers.
public class A
{
public int ID { get; set; }
public string Name { get; set; }
public virtual IList<B> B { get; set; }
}
public class B
{
public int A_ID { get; set; }
public string PhoneNumber { get; set; }
}
public class C
{
public string Name { get; set; }
public string PhoneNumber { get; set; }
}
Getting A class (which has B) details from the database and it needs to be mapped to Class C.
public class Activity
{
public IList<C> GetContacts(string name)
{
using (ModelEntities ctx = new ModelEntities())
{
Mapper.CreateMap<A, C>();
Mapper.CreateMap<B, C>();
var result =
ctx.A.SingleOrDefault(ss => ss.Name == name);
}
}
}
Can anyone help me to map using Automapper?
Thanks

AutoMapper cannot map from a single instances to multiple instances. It either must be instance to instance or enumerable to enumerable. The best path forward I can see is to simply map your IList<B> to IList<C> and then back-fill the Name property. Something along the lines of:
var c = Mapper.Map<IList<C>>(a.B);
c.ToList().ForEach(m => m.Name = a.Name);
However, unless your mapping is hella more complex than this, AutoMapper is overkill. You could simply do:
var c = a.B.Select(b => new C { Name = a.Name, PhoneNumber = b.PhoneNumber });

Related

Data is ignored at ThenInclude() for nested List property

I have more of a special case, and I am not sure what the reason is for what my issue is happening.
I have a class A with nested class B which also has a nested class C
public class A
{
[Key]
public int Id { get; set; }
public int Name { get; set; }
public List<B> Bs{ get; set; }
}
public class B
{
[Key]
public int Id { get; set; }
public int Name { get; set; }
public List<C> Cs{ get; set; }
[ForeignKey(nameof(A))]
public int AId{ get; set; }
}
public class C
{
[Key]
public int Id { get; set; }
public int Name { get; set; }
[ForeignKey(nameof(B))]
public int BId{ get; set; }
}
Now the problem that I have is that the values of class C aren't included into class B when class A is called alongside with Include(), respectively ThenInclude() methods.
The way values are brought from DB Tables is using a normal SQL Query combined with LINQ.
var query = from queryResult in _dbSet.Include(a => a.B).ThenInclude(B => b.C)
select new ResultModel()
{
Name = queryResult.Name
B = queryResult.B
}
The normal properties of B are being brought from db and mapped automatically to it, but the values for the nested List property aren't for some reason.
Do I need to make additional changes to select query in order for values of nested class C property be mapped into class B?
I hope the question was not too confusing.
The problem I had was the property being ignored because of not being included in the select. So the solution would be to change from
var query = from queryResult in _dbSet.Include(a => a.B).ThenInclude(B => b.C)
select new ResultModel()
{
Name = queryResult.Name
B = queryResult.B
}
to
var query = from queryResult in _dbSet.Include(a => a.B).ThenInclude(B => b.C)
select new ResultModel()
{
Name = queryResult.Name
B = queryResult.B.Select(x =>
new C {
Id = x.Id,
Name = x.Name,
}).ToList()
You can read more about this issue here.

EF Core Mapping Entities to Models

I'm trying to efficiently map entities on to models.
My entities are:
public class ParentEntity
{
public int Id { get; set; }
public string Name { get; set; }
public ChildEntity Child { get; set; }
}
public class ChildEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
and my models are:
public class ParentModel
{
public int Id { get; set; }
public string Name { get; set; }
public ChildModel Child { get; set; }
}
public class ChildModel
{
public int Id { get; set; }
public string Name { get; set; }
}
(In practice, there would be differences between these classes, but not here for simplification.)
I've written an extension method to do the mapping:
public static IQueryable<ParentModel> ToParentModel (this IQueryable<ParentEntity> parentEntities)
{
return parentEntities.Select(p => new ParentModel
{
Id = p.Id,
Name = p.Name,
Child = new ChildModel { Id = p.Child.Id, Name = p.Child.Name.ToLower()}
});
}
The ToLower() is there to highlight the problem.
I can run this with:
var parents = _context.Set<ParentEntity>().ToParentModel().ToArray();
The generated SQL is:
SELECT "p"."Id", "p"."Name", "c"."Id", lower("c"."Name") AS "Name"
FROM "Parents" AS "p"
LEFT JOIN "Children" AS "c" ON "p"."ChildId" = "c"."Id"
i.e. the lowercase processing is done in the database.
All good so far, except that the separation of concerns is not good. The code to initialize a ChildModel is in the same place as the code to initialize a ParentModel.
I try using a constructor in ChildModel:
public ChildModel(ChildEntity ent)
{
Id = ent.Id;
Name = ent.Name.ToLower();
}
and in the extension method:
return parentEntities.Select(p => new ParentModel
{
Id = p.Id,
Name = p.Name,
Child = new ChildModel (p.Child)
});
This works, but the generated SQL does not do contains a lower. The conversion to lowercase is done in the program.
Is there a way I can have by cake and eat it?
Can I still have my C# code converted to SQL, but still structure my C# code in a modular way?

AutoMapper c# runtime mapping depending on an object property

I would like to know, how could I, with AutoMapper, Map one Dto to multiple entities.
Lemme explain.
I've got one Dto, with an enum to describe its type (to avoid having multiple dtos)
Depending on that enum (RelationType here), I would like to map it to the correct Model (Entity, what ever, it's another object that I use in database).
public class BCardDto : IMappedDto
{
public long Id { get; set; }
public BCardRelationType RelationType { get; set; }
public long RelationId { get; set; }
}
Here are is my Model base:
public class BCardModel : IMappedDto
{
public long Id { get; set; }
}
And here the derived model :
public class CardBCardModel : BCardModel
{
// ormlite, ignore that
[Reference]
public CardModel Card { get; set; }
[ForeignKey(typeof(CardModel), ForeignKeyName = "fk_bcard_card")]
public long RelationId { get; set; }
}
How do I map my Dto to the correct Model depending on the enum i've given ?
(I don't wanna use Mapper.Map everywhere but I wanna let mapper do the runtime mapping job)
Here is how I do it for the Model -> Dto
cfg.CreateMap<CardBCardModel, BCardDto>()
.ForMember(s => s.RelationType, expression => expression.UseValue(BCardRelationType.Card))
.IncludeBase<BCardModel, BCardDto>();
Tell me if I do something wrong and explain me why please :)
Thanks by advance,
Blowa.
Let's say you have a setup wherein there is a base class and 2 classes which derive the base class:
public class ModelBase
{
public string Name { get; set; }
}
public class ModelOne : ModelBase { }
public class ModelTwo : ModelBase { }
Let's also say you have a DTO with an enum as below:
public class ModelDto
{
public string Name { get; set; }
public ModelType ModelType { get; set; }
}
public enum ModelType
{
One = 1,
Two = 2
}
So now the task is: How do I map the ModelDto to either ModelOne or ModelTwo depending on the value in ModelDto.ModelType property?
Here is how:
Mapper.Initialize(cfg => cfg.CreateMap<ModelDto, ModelBase>().ConstructUsing(x =>
{
switch (x.ModelType)
{
case ModelType.One:
return new ModelOne { Name = x.Name };
case ModelType.Two:
return new ModelTwo { Name = x.Name };
default:
throw new InvalidOperationException("Unknown ModelType...");
}
}));
Usage
var dto1 = new ModelDto { ModelType = ModelType.One, Name = "ModelOne" };
var dto2 = new ModelDto { ModelType = ModelType.Two, Name = "ModelTwo" };
var one = Mapper.Map<ModelBase>(dto1);
var two = Mapper.Map<ModelBase>(dto2);
Another way to do the mapping is by using dynamic:
public class PersonDto
{
public string Name { get; set; }
}
public class StudentDto : PersonDto
{
public int studentNumber { get; set; }
}
public class EmployeDto : PersonDto
{
public string EmployeId { get; set; }
}
public class Person
{
public string Name { get; set; }
}
public class Student : Person
{
public int StudentNumber { get; set; }
}
public class Employe : Person
{
public string EmployeId { get; set; }
}
Create Map by using:
Mapper.CreateMap<StudentDto, Student>();
Mapper.CreateMap<EmployeDto, Employe>();
Do the Mapping by:
try
{
var student = MapPerson((dynamic) studentDto);
var employe = MapPerson((dynamic) employeDto);
}
catch
{
throw new InvalidOperationException("Unknown ModelType...");
}
And define two Methods
public static Student MapPerson(StudentDto studentDto)
{
return Mapper.Map<StudentDto, Student>(studentDto);
}
public static Employe MapPerson(EmployeDto employeDto)
{
return Mapper.Map<EmployeDto, Employe>(employeDto);
}
The benefit is that you don't need a key and avoid the switch statement

Simple query performance

Let's say i have 4 Tables A,B,C and D
What is the best way to get list of B to be in a dropdown list.
Condition is that each record should have associated list of D1
so that if B has no D1 records i should not show it in the dropdown list
D1 is a child class of D
How i did it:
// number 2 to specify the selected A Table normally it's a variable
var b_Per_A = Uow.B.GetAll().Where(x => x.A_Id == 2).ToList();
var b_list = new List<B>();
foreach (var b in b_list)
{
var d =
Uow.D1.GetAll().Where(x => x.C.B_Id == b.Id);
if (!d.IsNullOrEmpty()) // extension method that checks if a collection is null or empty
{
b_list.Add(b);
}
}
It works but it's slow
UPDATE:
The signature of GetAll() is
IQueryable<T> GetAll();
UPDATE 2:
My Model is
public class A
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<B> ChildBs { get; set; }
}
public class B
{
public int Id { get; set; }
public string Name { get; set; }
public A ParentA { get; set; }
public IEnumerable<C> ChildCs { get; set; }
}
public class C
{
public int Id { get; set; }
public string Name { get; set; }
public B ParentB { get; set; }
public IEnumerable<D> ChildDs { get; set; }
}
public class D
{
public int Id { get; set; }
public string Name { get; set; }
public C ParentC { get; set; }
}
public class D1 : D
{
/// other properties
}
Given that you've confirmed you have navigation properties on your entity classes, this could be achieved in a single expression:
var result = Uow.B.GetAll()
.Where(b =>
b.ParentA.Id == 2 &&
b.ChildCs.Any() &&
b.ChildCs.SelectMany(c => c.ChildDs).Any())
.ToList();
This will push the filtering to your database rather than doing it in memory and should be a lot more efficient.
Also, I'm assuming that a parent navigation property will never be null and that child collections are always initialized.
Example here
put your var d = Uow.D.GetAll().Where(x => x.C.B_Id == b.Id); outside of your foreach. I also recommend you to use IEnumerable/ICollection instead of List and use for loop instead of foreach. Indexed looping is faster.

Entity Framework - customize navigation properties to abstract joins

I have looked through similar questions here, but not seen this specific scenario.
Using EF 6 Code First, I have three tables, A, B and C. The relationship is A => B = 1:M, and B=>C = 1:1
The end result in this schema is that there is an implicit 1:M between A and C.
I do NOT want the consumer of the Entity Framework model to know about B. Ideally they would have a 1:M Navigation property from A to C (and I'd like to be able to surface this Entity Model through Web API and OData as IQueryable)
How could I do this?
If I add a custom [NotMapped] property to A which a collection of C, I have no way of populating C within the getter of that property because the entity doesn't know about its context.
Anyone have any ideas as to how to implement an IQueryable where A has a navigation property to C and B is 'abstracted' out of existence?
EDIT
Attempted to put the following into the code first entity A:
[NotMapped]
public ICollection<C> Cs
{
get { return this.Bs.Select(b => b.C) as ICollection<C>; }
}
But got this error:
The navigation property 'C' is not a declared property on type 'A'. Verify that it has not been explicitly excluded from the model and that it is a valid navigation property.
Thanks.
Here is an example.
public static class OrderDAL
{
public static Order Get(int key)
{
using (var context = new AppContext())
{
var order = context.Orders.Include(a => a.OrderDetails.Select(b => b.Information)).FirstOrDefault(a => a.Id == key);
// Fills C.
order.OrderDetailAdditionalInformation = order.OrderDetails.Select(b => b.Information).ToArray();
// Hides information about B.
foreach (var information in order.OrderDetailAdditionalInformation)
{ information.OrderDetail = null; }
order.OrderDetails = null;
return order;
}
}
}
public class AppContext : DbContext
{
public DbSet<Order> Orders { get; set; }
}
// A
public class Order
{
public int Id { get; set; }
public DateTime Date { get; set; }
public ICollection<OrderDetail> OrderDetails { get; set; }
[NotMapped]
public ICollection<OrderDetailAdditionalInformation> OrderDetailAdditionalInformation { get; set; }
}
// B, one A many B
public class OrderDetail
{
public int Id { get; set; }
public int Qty { get; set; }
public string Item { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; }
public OrderDetailAdditionalInformation Information { get; set; }
}
// C, one B one C
public class OrderDetailAdditionalInformation
{
[ForeignKey("OrderDetail")]
public int Id { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Long { get; set; }
public OrderDetail OrderDetail { get; set; }
}

Categories