Automapper mapping clarification to satisfy design - c#

I am currently working with a DB table and I need to map extra properties from DAO to BE class.
I have to produce XML like the following:
<Zoos>
<Zoo>
<ZooId>234OI456<ZooId>
<Name>The Zoo</Name>
<Address>3456 Kramer</Address
<ZipCode></ZipCode>
<Animals>
<Animal Type="REPTILE">Cobra</Animal>
<Animals>
<Zoo>
<Zoos>
The extra columns in the db view are like so:
ANI_TYPE VARCHAR(20)
ANI_VALUE VARCHAR(20)
Previously when I didn't have the extra columns I mapped the values like this for each Zoo
Mapper.CreateMap<ZooDAO, ZooBE>()
.ForMember(d => d.ZooId, e => e.ZOO_ID))
.ForMember(d => d.Name, e => e.ZOO_NAME))
.ForMember(d => d.Address, e => e.ZOO_ADDRESS))
.ForMember(d => d.ZipCode, e => e.ZOO_ZIPCODE));
How would I go about mapping these 2 columns(ANI_TYPE, ANI_VALUE) so i can create the structure shown in the xml in regards to Animals?
I have a c# class for Animal type which has the following
public enum AnimalType
{
INVALID,
REPTILE,
MAMMAL,
INSECT
}
My C# class for the Animal looks like this but i guess it will require rework. Feel free to provide suggestions/examples:
Currently this is what my classes look like:
//BE class
public class Zoo
{
public int ZooId {get; set;}
public string Name { get; set; }
public string Address {get; set; }
public string ZipCode {get; set;}
public List<Animal> Animals {get; set;}
}
//BE Class
public class Animal
{
public string Type {get; set;}
public string Text { get; set; }
}
My DAO class
public class ZooDAO
{
public int ZOO_ID {get; set;}
public string ZOO_NAME { get; set; }
public string ZOO_ADDRESS {get; set; }
public string ZOO_ZIPCODE {get; set;}
public string ANI_TYPE {get; set;}
public string ANI_VALUE {get; set;}
}
I appreciate if someone can assist me with the above.
Thanks,

Related

Automapper define a custom variable used to determine the values of several properties from destination

I am using AutoMapper to map some DTOs to ui Models. However, one of my DTOs contains a field with serialized data (the serialized data represents another object with properties) that is used to determine the values of several properties of the model.
For example:
public class SourceDTo
{
public int Id {get; set;}
public int Name {get; set;}
public string SerializedDetails {get; set;}
}
public source DestinationModel
{
public int Id {get; set;}
public int Name {get; set;}
public string Address {get; set;}
public string AccountNr {get; set;}
}
So in this case, when mapping the source to destination, I would like to be able to deserialize the SerializedDetails field first and then use it in order to map Address and AccountNr with properties from that object, instead of deserializing it twice, for each property. Can this be done with AutoMapper?
May not be the most elegant solution YMMV:
CreateMap<Foo, Bar>()
.ForMember(dest => dest.prop1
, opt => opt.MapFrom((src, dest, destMember, context) => context.GetJsonObjectValue<string>("RawMessageJSON", "$.props.prop1")))
.ForMember(dest => dest.prop2
, opt => opt.MapFrom((src, dest, destMember, context) => context.GetJsonObjectValue<int>("RawMessageJSON", "$.props.prop2")));
Helper Extension Method for extracting Json path values from the JObject:
public static class AutomapperResolutionContextExtensions
{
public static T GetJsonObjectValue<T>(this ResolutionContext context, string key, string jsonTokenPath)
=> ((JObject)context.Items[key]).SelectToken(jsonTokenPath).ToObject<T>();
}
Here's the classes we are mapping:
public class Foo
{
public string RawMessageString { get; set;}
[NotMapped]
public JObject RawMessageJSON => JObject.Parse(RawMessageString);
}
public class Bar
{
public string prop1 { get; set; }
public int prop2 { get; set; }
}
When calling Map() we deserialize the string to a JObject and pass it in through in the Items Dictionary:
var bar = mapper.Map<Bar>(foo, opt => opt.Items["RawMessageJSON"] = foo.RawMessageJSON);

How to return this result using linq in entity framework( 2 foreign key mapping table)

I have a table that contains 2 foreign key that reference separately to 2 different table.
I would like to return the result of all person that has course of "Science".
How to retrieve the record back using LINQ?
This is what i gotten so far:
return
_ctx.Person
.Include(u => u.Course
.Where(ug=>ug.CourseName== "Science"));
This is not working as it shows the error.
The Include path expression must refer to a navigation property
defined on the type
public class Course
{
public int CourseID {get; set;}
public string CourseName {get; set;}
public virtual ICollection<Person> Persons { get; set; }
}
public class Person
{
public int PersonID {get; set;}
public virtual ICollection<Course> Courses { get; set; }
}
This is the mapping table. Only contains 2 foreign key from 2 different table.
I could not use this table inside the solution.As the code first won't generate this table as it doesn't contain it's own PK.
//This is not shown in the EntityFramework when generating Code First.
public class PersonCouseMap
{
public int PersonID {get; set;}
public int CourseID {get; set;}
}
Update : this works after I switched the entity.
return _ctx.Course
.Include(u=>u.Person)
.Where(ug=>ug.CourseName == "Sciene");
Anyone can explain why it won't work the another way round.
I need to display a List of Person who have course of "Science",
not Course Science that has a list of user.
The original query does not work because you've pushed the Where predicate inside the Include expression, which is not supported as indicated by the exception message.
The Include method is EF specific extension method used to eager load related data. It has nothing to do with the query filtering.
To apply the desired filter person that has course of "Science" you need Any based predicate since the Person.Courses is a collection:
return _ctx.Person
.Where(p => p.Courses.Any(c => c.CourseName == "Science"));
To include the related data in the result, combine it with Include call(s):
return _ctx.Person
.Include(p => p.Courses)
.Where(p => p.Courses.Any(c => c.CourseName == "Science"));
It looks like there is no relations between these two entites, you can establish a relationship by making the following changes to your code:
Here I am assuming that you want to establish Many-to-Many relationship between these two tables by having a third entity PersonCourseMap
public class Course
{
public int CourseID {get; set;}
public string CourseName {get; set;}
public virtual ICollection<CoursePersons> Courses { get; set; }
}
public class Person
{
public int PersonID {get; set;}
public virtual ICollection<PersonCourse> Courses { get; set; }
}
public class PersonCourseMap
{
public int PersonID {get; set;}
public int CourseID {get; set;}
public virtual ICollection<Person> Persons { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
After making above changes you can simply navigate through properties.
Include Foreign Key Mapping
public class Course
{
public int CourseID {get; set;}
public string CourseName {get; set;}
public virtual ICollection<Person> Person {get; set}
}
public class Person
{
public int PersonID {get; set;}
public virtual ICollection<Course> Course {get; set;}
}
using System.ComponentModel.DataAnnotation.Schema;
public class PersonCouseMap
{
[ForeignKey("Person")]
public int PersonID {get; set;}
[ForeignKey("Course")]
public int CourseID {get; set;}
public virtual ICollection<Person> Person {get; set;}
public virtual ICollection<Course> Course {get; set;}
}

Automapper ignore object mapping based on condition

My problem could be schematized like this :
I have a domain class represented like this
public class DomainClass {
public DateTime Begin {get; set;}
public DateTime End {get; set;}
public int Type {get;set}
}
And I have two distincts tables in database that matches different type :
public class TypeClassOne {
public DateTime Begin {get; set;}
public DateTime End {get; set;}
}
and
public class TypeClassTwo {
public DateTime Begin {get; set;}
public DateTime End {get; set;}
}
How can I configure AutoMapper to have a mapping like this
var config = new MapperConfiguration(c =>
{
c.CreateMap<DomainClass, TypeClassOne>()
.??? <= I would like OnlyIf(source => source.Type == 1)
}
I've tried ForMember, but it doesn't works at class level...
Anyone tried to do this ?

Best way to return count of nested objects without list them

I need to count a list of nested objects, but don't want to return the nested objects.
I have the classes:
public class Categoria
{
public int ID {get; set}
public List<Produto> produto {get; set;}
public int produtoCount {get; set;}
}
public class Produto
{
public ID {get; set}
public string data {get; set;}
}
I have tried using produtoCount in Categoria class, but it only has value when I use a Include of Produto class, like this:
(new Categoria()).AsQueryable().Include(p => p.Produto).ToList();
Is it possible?
Sure it's possible:
DbContext.Categorias
.Where(c => c.Id == id)
.Select(c => new
{
// Assuming you also want the Categoria
Categoria = c,
ProdutoCount = c.produto.Count()
})
.FirstOrDefault();
Please try below code. It may help you.
public class Categoria
{
public int ID {get; set}
private List<Produto> produto {get; set;}
public int produtoCount {get {return produto.Count();} }
}
public class Produto
{
public ID {get; set}
public string data {get; set;}
}
Thanks,
Amit Prajapati

How to avoid circular references with AutoMapper?

I have the following models (and corresponding DTOs):
public class Link
{
public int Id {get; set;}
public int FirstLinkId {get; set;}
public int SecondLinkId {get; set;}
public virtual Link FirstLink {get; set;}
public virtual Link SecondLInk {get; set;}
}
public class OtherObject
{
public int Id {get; set;}
public int LinkId {get; set;}
public string Name {get; set;}
public virtual Link Link {get; set;}
}
In my scenario, I can have a Link object where FirstLink and/or SecondLink can be null, references to other objects, or references to the same object.
Now I want to load an OtherObject entity from the db using EF. I load the entity itself and also the Link object associated with it. This is done perfectly by EF.
In this particular case, both FirstLink and SecondLink are the same as Link, therefore, when automapping from model to dto it just keeps on mapping into oblivion.
My mapping is:
Mapper.CreateMap<OtherObject, OtherObjectDto>().Bidirectional()
.ForMember(model => model.LinkId, option => option.Ignore());
where Bidirectional() is this extension:
public static IMappingExpression<TDestination, TSource> Bidirectional<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
return Mapper.CreateMap<TDestination, TSource>();
}
Is there way to tell Automapper not to map further down the tree in this case?
The way I would handle this is to create separate DTO objects for the children:
public class Employee
{
public int Id {get; set;}
public string Name { get; set; }
public Employee Supervisor {get; set; }
}
public class EmployeeDto {
public int Id {get; set;}
public string Name { get; set; }
public SupervisorDto Supervisor { get; set; }
public class SupervisorDto {
public int Id {get; set;}
public string Name { get; set; }
}
}
Mapper.CreateMap<Employee, EmployeeDto>();
Mapper.CreateMap<Employee, EmployeeDto.SupervisorDto>();
Don't let your DTOs be recursive/self-referential. Be explicit in your structure on how deep you want it to go.
EF can't do recursive joins, you're only doing one level, so don't make your DTOs go nuts with infinitely deep relationships. Be explicit.

Categories