Use Automapper to flatten sub-class of property - subclass

Given the classes:
public class Person
{
public string Name { get; set; }
}
public class Student : Person
{
public int StudentId { get; set; }
}
public class Source
{
public Person Person { get; set; }
}
public class Dest
{
public string PersonName { get; set; }
public int? PersonStudentId { get; set; }
}
I want to use Automapper to map Source -> Dest.
This test obviously fails:
Mapper.CreateMap<Source, Dest>();
var source = new Source() { Person = new Student(){ Name = "J", StudentId = 5 }};
var dest = Mapper.Map<Source, Dest>(source);
Assert.AreEqual(5, dest.PersonStudentId);
What would be the best approach to mapping this given that "Person" is actually a heavily used data-type throughout our domain model.
Edit: The intent is to persist the "Dest" objects which will have fields defined for all properties of the sub-types of "Person". Hence we could have source objects like the following and would prefer not to have to create Dest objects for every possible combination of "Person" sub-classes:
public class Source2
{
public Person Value1 { get; set; }
public Person Value2 { get; set; }
public Person Value3 { get; set; }
public Person Value4 { get; set; }
public Person Value5 { get; set; }
}

Well using Jimmy's suggestion I've settled on the following solution:
public class Person
{
public string Name { get; set; }
}
public class Student : Person
{
public int StudentId { get; set; }
}
//all subtypes of person will map to this dto
public class PersonDto
{
public string Name { get; set; }
public int? StudentId { get; set; }
}
public class Source
{
public Person Person { get; set; }
}
public class DestDto
{
public PersonDto Person { get; set; }
}
public class Dest
{
public string PersonName { get; set; }
public int? PersonStudentId { get; set; }
}
[TestFixture]
public class RandomTests
{
[Test]
public void Test1()
{
Mapper.CreateMap<Person, PersonDto>();
Mapper.CreateMap<Student, PersonDto>();
Mapper.CreateMap<Source, DestDto>();
Mapper.CreateMap<DestDto, Dest>();
var source = new Source() { Person = new Student() { Name = "J", StudentId = 5 } };
var destDto = Mapper.Map<Source, DestDto>(source);
var destFinal = Mapper.Map<DestDto, Dest>(destDto);
Assert.AreEqual(5, destFinal.PersonStudentId);
}
}
Would love to hear suggestions/improvements.

Related

automapper issue in nested entity

Entity:
public class AccountEntity: MasterEntity
{
public AccountType AccountType { get; set; }
public DepartmentEntity Department { get; set; }
public string LeadSource { get; set; }
public ResourceEntity AccountManager { get; set; }
public AccountQualificationStatus QualificationStatus { get; set; }
}
public class DepartmentEntity : MasterEntity
{
}
public class ResourceEntity : MasterEntity
{
public string Email { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
}
public abstract class MasterEntity: Entity
{
public string Name { get; set; }
public string Code { get; set; }
}
Model
public class Accounts
{
private string depname;
public int Id { get; set; }
public string Name { get; set; }
public int Code { get; set; }
public string AccountType { get; set; }
public string Department { get; set; }
}
public AccountMappingProfile()
{
CreateMap<Account, AccountTest>().ReverseMap();
CreateMap<Opportunity, OpportunityEntity>().ReverseMap();
CreateMap<Accounts, AccountEntity>().ReverseMap();
CreateMap<Resource, ResourceEntity>().ReverseMap();
CreateMap<Projects, ProjectEntity>().ReverseMap();
}
public static class AccountMapper
{
private static readonly Lazy<IMapper> Lazy = new Lazy<IMapper>(() =>
{
var config = new MapperConfiguration(cfg => {
cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
cfg.AddProfile<AccountMappingProfile>();
});
var mapper = config.CreateMapper();
return mapper;
});
public static IMapper Mapper => Lazy.Value;
}
I am able to fetch resource details .while I am fetching accounts with department and resources, the value for department is giving me null after automapping.
public async Task<IEnumerable<Accounts>> GetAccounts()
{
try
{
var accounts = await _context.Account.Include(c=>c.Director).ToListAsync();
// accounts = await _context.Account.Include(c => c.Department).ToListAsync();
return AccountMapper.Mapper.Map<List<Accounts>>(accounts);
}
}
controller method:
public async Task<IEnumerable<Accounts>> GetClients()
{
var result = await _repository.GetAccounts();
return result;
}
if I am uncommenting the code to include department, its is giving the object name.
I need to get the department details as well. how can i get that.Department entity is inheriting master entity.

how can I Select One row where max StartDate in list objects using linq

how can I Select One row json where max StartDate in list objects
using linq tolist
{"itmes":[
{"StartDate":"20190901185703","Name":"A1","Id":"1","EndDate":"20190930235959"}
,{"StartDate":"20190903181510","Name":"A2","Id":"2","EndDate":"20190909235959"}
,{"StartDate":"20190906005152","Name":"A3","Id":"3","EndDate":"20191006235959"}
,{"StartDate":"20190714181313","Name":"A4","Id":"4","EndDate":"20991231235959"}
],"Code":"0","text":"success"}
public class Query
{
public class itmes
{
public string StartDate { get; set; }
public string Name { get; set; }
public string Id { get; set; }
public string EndDate { get; set; }
}
public class RootObject
{
public List<itmes> itmes { get; set; }
public string Code { get; set; }
public string text { get; set; }
}
}
var result = JsonConvert.DeserializeObject<List<Query.RootObject>>(json);
Expected output This row tolist
{"StartDate":"20190906005152","Name":"A3","Id":"3","EndDate":"20191006235959"}
Firstly you cannot cast and object to collection so it should be Query.RootObject instead of this List<Query.RootObject> ,Secondly im not sure why did you make class inside a class you can do it independently without the query class
You classes would be
public class itmes
{
public string StartDate { get; set; }
public string Name { get; set; }
public string Id { get; set; }
public string EndDate { get; set; }
}
public class RootObject
{
public List<itmes> itmes { get; set; }
public string Code { get; set; }
public string text { get; set; }
}
var json = "{\"itmes\":[ {\"StartDate\":\"20190901185703\",\"Name\":\"A1\",\"Id\":\"1\",\"EndDate\":\"20190930235959\"} ,{\"StartDate\":\"20190903181510\",\"Name\":\"A2\",\"Id\":\"2\",\"EndDate\":\"20190909235959\"} ,{\"StartDate\":\"20190906005152\",\"Name\":\"A3\",\"Id\":\"3\",\"EndDate\":\"20191006235959\"} ,{\"StartDate\":\"20190714181313\",\"Name\":\"A4\",\"Id\":\"4\",\"EndDate\":\"20991231235959\"} ],\"Code\":\"0\",\"text\":\"success\"}";
var result = JsonConvert.DeserializeObject<RootObject>(json);
var row = result.itmes.Where(p=> p.StartDate == result.itmes.Max(q => q.StartDate)).ToList();

Why Entity Framework replaces provided value with a new incremental value

I'm trying to work my way through a Dot Net Core MVC project, but I am facing a problem I couldn't solve.
And honestly, I didn't know what to search for.
The project is a simple vehicle registry,
Each vehicle has a Make, a Model, a some Features.
Here are the domain models:
public class Make
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Model> Models { get; set; }
public Make()
{
Models=new Collection<Model>();
}
}
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
public Make Make { get; set; }
public int MakeId { get; set; }
public Model()
{
Make=new Make();
}
}
public class Feature
{
public int Id { get; set; }
public string Name { get; set; }
}
[Table(name:"VehicleFeatures")]
public class VehicleFeature
{
public int VehicleId { get; set; }
public int FeatureId { get; set; }
public Feature Feature { get; set; }
public Vehicle Vehicle { get; set; }
public VehicleFeature()
{
Feature=new Feature();
Vehicle=new Vehicle();
}
}
public class Vehicle
{
public int Id { get; set; }
public Model Model { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ModelId { get; set; }
public ICollection<VehicleFeature> VehicleFeatures { get; set; }
public string ContactName { get; set; }
public string ContactPhone { get; set; }
public string ContactEmail { get; set; }
public DateTime LastUpdate { get; set; }
public bool IsRegistered { get; set; }
public Vehicle()
{
Model=new Model();
VehicleFeatures=new Collection<VehicleFeature>();
}
}
The problem is that when I send the following request to the corresponding controller, EF is replacing the provided value with a new incremental value.
The request I send:
{
"ModelId":10,
"VehicleFeatures":[
{"featureId":45},
{"featureId":46}
],
"ContactName":"Alice",
"ContactPhone":"1234",
"ContactEmail":"Alice#local",
"LastUpdate":"1980-01-01",
"IsRegistered":true
}
And this is what happens:
The controller receives correct values,
Correct values are added to the context,
And then suddenly all hell breaks loose.
This is the controller code:
[HttpPost]
public async Task<IActionResult> CreateVehicle([FromBody] Vehicle v)
{
context.Vehicles.Add(v);
await context.SaveChangesAsync();
return Ok(v.ModelId);
}
What am I missing here?

Where IN clause in LINQ

My class have a list of another class.
public class CustomerRequest : BaseEntity
{
[Key]
public int Id { get; set; }
public int Code { get; set; }
public virtual List<TechnicalOfficer> TechnicalOfficers { get; set; }
}
public class TechnicalOfficers : BaseEntity
{
[Key]
public int Id { get; set; }
public int Code { get; set; }
}
I want to select all CustomerRequest that TechnicalOfficers are contains special id.
I want to select all CustomerRequest that TechnicalOfficers are contains special id.
Use Any (or perhaps All) with Contains.
var specialIds = new[] { 1, 2, 3 };
var customerRequests = CustomerRequests
.Where(cr => cr.TechnicalOfficers.Any(to => specialIds.Contains(to.Id)));

EF-code-first many-to-many related objects generates two different SQL tables (MVC3)

I have read this great Q&A and I tried to make something similar. My Model classes are:
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Course> CoursesAttending { get; set; }
public Person()
{
this.CoursesAttending = new List<Course>();
}
}
public class Course
{
public int CourseId { get; set; }
public string Title { get; set; }
public ICollection<Person> Students { get; set; }
}
public class PersonCourse
{
[Key, Column(Order = 0)]
public int PersonID { get; set; }
[Key, Column(Order = 1)]
public int CourseID { get; set; }
public virtual Person Student { get; set; }
public virtual Course StudentCourse { get; set; }
public int Mark { get; set; }
public string Comment { get; set; }
}
public class SchoolContext : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Person> People { get; set; }
public DbSet<PersonCourse> PersonCourseLinks { get; set; }
public SchoolContext()
: base("ManyToManyTest")
{
}
}
Now, I try to add a new person and add a new course to his courses list:
[HttpPost]
public ActionResult Create(Person person)
{
if (ModelState.IsValid)
{
Course studentCourse;
try
{
studentCourse = db.Courses.ToList<Course>().First();
}
catch
{
studentCourse = new Course() { Title = "HTML" };
}
person.CoursesAttending.Add(studentCourse);
db.People.Add(person);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(person);
}
Everything goes well, but when I open my created database, I see there are 2 link tables for the Person and Course classes - PersonCourses(with fields: PersonID, CourseID, Mark, Comment) and PersonCourse1(with fields: PersonID, CourseID), and only PersonCourse1 has rows (actually one row). Why does it happen? Did I do something not correct? I expect to see only one link table - the PersonCourses table...
I expect to see only one link table - the PersonCourses table
Then you have to link PersonCourse entity with Person entity though CoursesAttending navigational property. Same thing has to be done to Course entity.
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<PersonCourse> CoursesAttending { get; set; }
public Person()
{
this.CoursesAttending = new List<PersonCourse>();
}
}
public class Course
{
public int CourseId { get; set; }
public string Title { get; set; }
public ICollection<PersonCourse> Students { get; set; }
}
Give foreign key associations to the foreign key properties in your gerund table:
public class PersonCourse
{
[Key, Column(Order = 0)]
public int PersonID { get; set; }
[Key, Column(Order = 1)]
public int CourseID { get; set; }
[ForeignKey("PersonID")]
public virtual Person Student { get; set; }
[ForeignKey("CourseID")]
public virtual Course StudentCourse { get; set; }
public int Mark { get; set; }
public string Comment { get; set; }
}
Just so you now why your code wasn't working as expected:
public class PersonCourse
{
[Key, Column(Order = 0)]
public int PersonID { get; set; }
[Key, Column(Order = 1)]
public int CourseID { get; set; }
public virtual Person Person { get; set; } // Follow built in conventions
public virtual Course Course { get; set; }
public int Mark { get; set; }
public string Comment { get; set; }
}
This now follows the built in Entity Framework convention of having
public int [ClassName][Id] or [Id] //Database Foreign Key column
and
public NavigationProperty [ClassName] // Navigation Property

Resources