Reusing common mapping code across common DTO classes - c#

I have 2 DTO classes that have multiple common properties, I'm trying to avoid having to repeat myself when writing mapping code for entity to DTO conversion, I'm wondering how I could achieve this, I have a feeling I need to probably use a Func or Action delegate to achieve this. For example I have 2 classes StudentDTO and EmployeeDTO:
public class StudentDTO : PersonDTO
{
public int CourseId { get; set; }
//other properties
}
public class EmployeeDTO : PersonDTO
{
public int OccupationId { get; set; }
//other properties
}
and both naturally inherit from PersonDTO:
public class PersonDTO
{
public int Id { get; set; }
public string FirstName { get; set; }
public string FamilyName { get; set; }
public int Age { get; set; }
}
How could I reuse the mapping code that maps the common properties? Thanks.

You probably can do something like this (very basic and not elegant):
(note Entity can off course be a DataReader, DataSet etc.)
public class Entity
{
public string FirstName { get; set; }
public string FamilyName { get; set; }
public int CourseId { get; set; }
public int OccupationId { get; set; }
}
public class BaseDto
{
}
public class PersonDto : BaseDto
{
public string FirstName { get; set; }
public string FamilyName { get; set; }
public static void Map(Entity entity, PersonDto personDto)
{
personDto.FirstName = entity.FirstName;
personDto.FamilyName = entity.FamilyName;
}
}
public class StudentDto : PersonDto
{
public int CourseId { get; set; }
public static StudentDto Map(Entity entity)
{
var studentDto = new StudentDto { CourseId = entity.CourseId };
// ..can call map to PersonDto if you want
return studentDto;
}
}
public class EmployeeDto : PersonDto
{
public int OccupationId { get; set; }
public static EmployeeDto Map(Entity entity)
{
var employeeDto = new EmployeeDto() { OccupationId = entity.OccupationId };
// ..can call map to PersonDto if you want
return employeeDto;
}
}
public class Mapper<TDto>
where TDto : BaseDto
{
private TDto _dto;
private readonly Entity _entity;
public Mapper(Entity entity)
{
_entity = entity;
}
public Mapper<TDto> Map(Func<Entity, TDto> map)
{
_dto = map(_entity);
return this;
}
public Mapper<TDto> Map<TBaseDto>(Action<Entity, TBaseDto> map)
where TBaseDto : BaseDto
{
map(_entity, _dto as TBaseDto);
return this;
}
public TDto Result
{
get { return _dto; }
}
}
class Program
{
static void Main(string[] args)
{
var studentEntity = new Entity() { FirstName = "John", FamilyName = "Doe", CourseId = 1 };
var studentDto = new Mapper<StudentDto>(studentEntity)
.Map(StudentDto.Map)
.Map<PersonDto>(PersonDto.Map)
.Result;
}
}

Use a library.. that's what they are there for!
Automapper
ValueInjecter
In Automapper, your above mapping becomes incredibly simple:
Mapper.CreateMap<EmployeeDTO, StudentDTO>();
Mapper.CreateMap<StudentDTO, EmployeeDTO>();
..then when you want to map:
var studentInstance = ...; // go get student instance
var employee = Mapper.Map<Employee>(studentInstance);

Related

Project a name-value list into an object

I want to be able to save an arbitrary flat object into the name-value list.
public class NameValueListEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[InverseProperty(nameof(NameValueListContentEntity.Entity))]
public ICollection<NameValueListContentEntity> Content { get; set; }
}
public class NameValueListContent
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("entity_fk")]
public NameValueListEntity Entity { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
public class ObjectToSave
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
I could use reflection to manually assemble/parse the list, but it will create a lot of overhead. Lots of NameValueListContent objects will be needlessly created both during the saving and the reading. Could it somehow be omitted? Especially during the reading, which is very performance-sensitive in my case.
Assume you have a AppDbContext class that holds your NameValueListContent class objects named as NVListContents. You can read and write the name-value list of objects by doing the following:
public class AppDbContext : DbContext
{
public DbSet<NameValueListContent> NVListContents { get; set; }
public AppDbContext()
: base()
{ }
}
public class SomeClass
{
private AppDbContext context { get; set; }
public SomeClass(AppDbContext _context)
{
context = _context;
}
public List<ObjectToSave> ReadObjects()
{
return context.NVListContents
.Select(nvlc => new ObjectToSave { Prop1 = nvlc.Name, Prop2 = nvlc.Value
}).ToList();
}
public bool WriteObjects(int id, string name, string value)
{
var query = context.NVListContents
.FirstOrDefault(nvlc => nvlc.Id == id);
if(query != null)
{
query.Name = name;
query.Value = value;
context.Update(query);
context.SaveChanges();
return true;
}
else
{
return false;
}
}
}
Hope, this answers to your question.

Nested Expression method call in Linq.Select()

I use .Select(i=> new T{...}) after every db hit manually to convert my entity objects into DTO object. Here are some sample entities and DTOS
User Entity;
public partial class User
{
public int Id { get; set; }
public string Username { get; set; }
public virtual UserEx UserEx { get; set; }
}
User DTO;
public class UserDTO
{
public int Id { get; set; }
public string Username { get; set; }
}
UserEx entity;
public class UserEx
{
public int UserId { get; set; }
public string MyProperty1 { get; set; }
public virtual User User { get; set; }
}
UserEx DTO;
public class UserExDTO
{
public int MyProperty1 { get; set; }
public UserDTO UserModel { get; set; }
}
My Conversion Expression Methods;
public static class ConversionExpression
{
public static Expression<Func<UserEx, UserExDTO>> GetUserExDTOConversionExpression()
{
return userEx => new UserExDTO
{
MyProperty1 = userEx.MyProperty1,
UserModel = new UserDTO
{
Id = userEx.User.Id,
Username = userEx.User.Username
}
};
}
public static Expression<Func<User, UserDTO>> GetUserDTOConversionExpression()
{
return user => new UserDTO
{
Id = user.Id,
Username = user.Username
};
}
}
And my current usage for UserDTO;
myContext.Users
.Select(ConversionExpression.GetUserDTOConversionExpression())
.ToList();
for UserExDTO;
myContext.UserExes
.Select(ConversionExpression.GetUserExDTOConversionExpression())
.ToList();
Apologize for long introduction, now here is my question ;
I need to group
new UserDTO
{
Id = userEx.User.Id,
Username = userEx.User.Username
}
due to single point of concerns. So I want something like this;
public static Expression<Func<UserEx, UserExDTO>> GetUserExDTOConversionExpression()
{
return userEx => new UserExDTO
{
MyProperty1 = userEx.MyProperty1,
//this line does not behave like the other one
UserModel = userEx.User.GetUserDTOConversionExpression()
};
}
Is there any way to do that or should I write down every expression individual and nonrelated to similar needs?
I've solved my issue and beyond only with NeinLinq. Here is my solution;
You need to remove nested declarations first.
public static Expression<Func<UserEx, UserExDTO>> GetUserExDTOConversionExpression()
{
return userEx => new UserExDTO
{
MyProperty1 = userEx.MyProperty1
//We removed other model declaration here.
};
}
Then use To method of NeinLinq to define the translation;
public static Expression<Func<UserEx, UserExDTO>> GetUserExDtOCompbinedExpression()
{
//Translate() and To() methods do all the job
return GetUserDTOConversionExpression().Translate()
.To(userEx => userEx.User, userExDTO => userExDTO.UserModel, GetUserExDTOConversionExpression());
}

Ignore Mapping of inner list property in AutoMapper --EF6,C#

I have a collection property of DTO like this
public ICollection<Applicant> Applicants{get;set;}
Applicant Model
public class Applicant
{
public int Id{get;set;}
public string name{get;set;}
public ICollection<ApplicantSkillsVM> ApplicantSkills { get; set; }
}
public class ApplicantSkillsVM
{
public int Id {get;set;}
public Skill skill{get;set;}
}
I want to map my List<iApplicant> DTO to entity given that I want to take ApplicantSkillsVM but ignore skill inside ApplicantSkillsVM.
I have a model which is list List<Applicant> and that contains another list List<ApplicantSkillsVM> and ApplicantSkillsVM has a property skill. I want to ignore this (skill) while mapping. Its simple.
How can I do this in latest the AutoMapper version with EF6?
Here a running sample:
internal class Program
{
#region Methods
private static void Main(string[] args)
{
// Configure the mappings
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ApplicantSkillVM, ApplicantSkill>().ForMember(x => x.Skill, x => x.Ignore()).ReverseMap();
cfg.CreateMap<ApplicantVM, Applicant>().ReverseMap();
});
var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
var mapper = config.CreateMapper();
ApplicantVM ap = new ApplicantVM
{
Name = "its me",
ApplicantSkills = new List<ApplicantSkillVM>
{
new ApplicantSkillVM {SomeInt = 10, SomeString = "test", Skill = new Skill {SomeInt = 20}},
new ApplicantSkillVM {SomeInt = 10, SomeString = "test"}
}
};
List<ApplicantVM> applicantVms = new List<ApplicantVM> {ap};
// Map
List<Applicant> apcants = Mapper.Map<List<ApplicantVM>, List<Applicant>>(applicantVms);
}
#endregion
}
/// Your source classes
public class Applicant
{
#region Properties
public List<ApplicantSkill> ApplicantSkills { get; set; }
public string Name { get; set; }
#endregion
}
public class ApplicantSkill
{
#region Properties
public Skill Skill { get; set; }
public int SomeInt { get; set; }
public string SomeString { get; set; }
#endregion
}
// Your VM classes
public class ApplicantVM
{
#region Properties
public List<ApplicantSkillVM> ApplicantSkills { get; set; }
public string Description { get; set; }
public string Name { get; set; }
#endregion
}
public class ApplicantSkillVM
{
#region Properties
public Skill Skill { get; set; }
public int SomeInt { get; set; }
public string SomeString { get; set; }
#endregion
}
public class Skill
{
#region Properties
public int SomeInt { get; set; }
#endregion
}
}
Initially my model ApplicantSkillsVM didnt have reference Id for Skill which should be nullable
So my model had to look like
public class ApplicantSkillsVM{
public int Id {get;set;}
public int? skillId{get;set;} //updated property
public Skill skill{get;set;}
}
The problem resolved

Error Mapping Types using automapper

I'm new to automapper and I'm trying very simple map. I have the following class and I', trying to map personmodel to person.
It keeps failing at orders. Sorry if this silly but I could not figure out. I removed all properties on orders to see what is wrong
public class Person
{
public Person()
{
Orders = new List<Orders>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public List<Orders> Orders { get; set; }
}
public class Orders
{
public string OrderName { get; set; }
}
public class PersonModel
{
public PersonModel()
{
Orders = new List<OrderModel>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public List<OrderModel> Orders { get; set; }
}
public class OrderModel
{
public string OrderName { get; set; }
}
I have the mapper defined as Mapper.Initialize(x => x.CreateMap<PersonModel, Person>());
The error that I get is:
Error mapping types.
Mapping types:
PersonModel -> Person
PersonModel -> Person
Type Map configuration:
PersonModel -> Person
PersonModel -> Person
Property:
Orders
Create Map between Orders and OrderModel.
Mapper.CreateMap<Orders, OrderModel>().ReverseMap();
I use this code for map a list<>
var result = NewMapperHelper.MapList<OrderModel, Orders>(YourSource);
and:
public static class NewMapperHelper
{
public static List<TDestination> MapList<TDestination, TSource>
(List<TSource> entity)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TSource, TDestination>().ReverseMap();
});
var _mapper = config.CreateMapper();
return _mapper.Map<List<TDestination>>(entity);
}
}

Convert to using Generics

I've tried several times and I keep getting myself into a loop. How would you change this code to a generics based approach? This code follows the Gang of Four Composite Pattern.
public abstract class AdjacencyTreeBase
{
public AdjacencyTreeBase(int entityId, int entityTypeId)
{
EntityId = entityId;
EntityTypeId = entityTypeId;
}
public long? Id { get; set; }
public int? SystemId { get; set; }
public int EntityId { get; set; }
public int EntityTypeId { get; set; }
public bool? isActive { get; set; }
public long? lft { get; set; }
public long? rgt { get; set; }
public abstract void AddChild(AdjacencyTreeBase c);
public abstract void RemoveChild(AdjacencyTreeBase c);
public abstract List<AdjacencyTreeBase> ListChildren();
public abstract void AddChildren(List<AdjacencyTreeBase> c);
public abstract void ReplaceChildren(List<AdjacencyTreeBase> c);
}
public class AdjacencyTree : AdjacencyTreeBase
{
private List<AdjacencyTreeBase> _children = new List<AdjacencyTreeBase>();
public List<AdjacencyTreeBase> Children { get { return _children; } set { _children = value; } }
public AdjacencyTree(int entityId, int entityTypeId) : base(entityId, entityTypeId) { }
public override void AddChild(AdjacencyTreeBase component)
{
_children.Add(component);
}
public override void AddChildren(List<AdjacencyTreeBase> c)
{
_children = c;
}
public override void ReplaceChildren(List<AdjacencyTreeBase> c)
{
_children = c;
}
public override void RemoveChild(AdjacencyTreeBase component)
{
_children.Remove(component);
}
public override List<AdjacencyTreeBase> ListChildren()
{
return _children;
}
}
public class AdjacencyAgency : AdjacencyTree
{
public string agency_name { get; set; }
public string customer_number { get; set; }
public string agency_type { get; set; }
public AdjacencyAgency(int entityId, int entityTypeId) : base(entityId, entityTypeId)
{
}
}
public class AdjacencyUser : AdjacencyTree
{
public string officer_number { get; set; }
public string last_name { get; set; }
public string first_name { get; set; }
public string middle_initial { get; set; }
public AdjacencyUser(int entityId, int entityTypeId) : base(entityId, entityTypeId)
{
}
}
public class AdjacencyClient : AdjacencyTree
{
public string last_name { get; set; }
public string first_name { get; set; }
public string middle_initial { get; set; }
public string ssn { get; set; }
public AdjacencyClient(int entityId, int entityTypeId) : base(entityId, entityTypeId)
{
}
}
A sample of instantiating this object map:
public List<AdjacencyTreeBase> CreateSample()
{
// build bottom of tree objects...
var client1 = new AdjacencyClient(1, 4)
{
first_name = "Pic'nic",
last_name = "Basket #1",
ssn = "123-45-6789"
};
var client2 = new AdjacencyClient(2, 4)
{
first_name = "Pic'nic",
last_name = "Basket #2",
ssn = "234-56-7890"
};
var client3 = new AdjacencyClient(3, 4)
{
first_name = "Bear",
last_name = "Cave",
ssn = "345-67-8901"
};
var client4 = new AdjacencyClient(4, 4)
{
first_name = "Picnic",
last_name = "Table",
ssn = "456-78-9012"
};
// build the next level up and add the children...
var officer1 = new AdjacencyUser(1, 3)
{
first_name = "Yogi",
last_name = "Bear",
officer_number = "YB123"
};
officer1.AddChild(client1);
officer1.AddChild(client2);
var officer2 = new AdjacencyUser(2, 3)
{
first_name = "Park",
last_name = "Ranger",
officer_number = "PR123"
};
officer2.AddChild(client3);
officer2.AddChild(client4);
// build the top of the tree and add the middle children...
var agencyThatAlreadyExists = new AdjacencyAgency(1, 2)
{
agency_name = "Jellystone",
agency_type = "Park",
};
agencyThatAlreadyExists.AddChild(officer1);
agencyThatAlreadyExists.AddChild(officer2);
return agencyThatAlreadyExists;
}
While my sample is pretty simple, our entity structure is not quite so simple. We currently have 7 different entities and pretty much any type of entity can be a child of any type of entity and its siblings could be various types as well.
TIA
EDIT:
To try to clarify: The children (and children of children) can be of any entity type (agency, user, officer, client, etc). While all entities have a base of properties in common, the rest of each object is different from one another. When pulling from the database, I may make a request for an agency and want the entire hierarchy underneath that one agency. Direct descendants could include all types, the each child could have descendants which include all types. Very messy, very flexible.
This works to make your class hierarchy strongly-typed & comply with the CreateSample code:
public abstract class AdjacencyTreeBase<T> where T : AdjacencyTreeBase<T>
{
public AdjacencyTreeBase(int entityId, int entityTypeId)
{
EntityId = entityId;
EntityTypeId = entityTypeId;
}
public long? Id { get; set; }
public int? SystemId { get; set; }
public int EntityId { get; set; }
public int EntityTypeId { get; set; }
public bool? isActive { get; set; }
public long? lft { get; set; }
public long? rgt { get; set; }
public abstract void AddChild(T c);
public abstract void RemoveChild(T c);
public abstract List<T> ListChildren();
public abstract void AddChildren(List<T> c);
public abstract void ReplaceChildren(List<T> c);
}
public abstract class AdjacencyTree : AdjacencyTreeBase<AdjacencyTree>
{
private List<AdjacencyTree> _children = new List<AdjacencyTree>();
public List<AdjacencyTree> Children { get { return _children; } set { _children = value; } }
public AdjacencyTree(int entityId, int entityTypeId) : base(entityId, entityTypeId) { }
public override void AddChild(AdjacencyTree component)
{
_children.Add(component);
}
public override void AddChildren(List<AdjacencyTree> c)
{
_children = c;
}
public override void ReplaceChildren(List<AdjacencyTree> c)
{
_children = c;
}
public override void RemoveChild(AdjacencyTree component)
{
_children.Remove(component);
}
public override List<AdjacencyTree> ListChildren()
{
return _children;
}
}
public class AdjacencyAgency : AdjacencyTree
{
public string agency_name { get; set; }
public string customer_number { get; set; }
public string agency_type { get; set; }
public AdjacencyAgency(int entityId, int entityTypeId) : base(entityId, entityTypeId)
{
}
}
public class AdjacencyUser : AdjacencyTree
{
public string officer_number { get; set; }
public string last_name { get; set; }
public string first_name { get; set; }
public string middle_initial { get; set; }
public AdjacencyUser(int entityId, int entityTypeId) : base(entityId, entityTypeId)
{
}
}
public class AdjacencyClient : AdjacencyTree
{
public string last_name { get; set; }
public string first_name { get; set; }
public string middle_initial { get; set; }
public string ssn { get; set; }
public AdjacencyClient(int entityId, int entityTypeId) : base(entityId, entityTypeId)
{
}
}
Then CreateSample needs to be modified like this:
public List<AdjacencyTree> CreateSample()
{
// build bottom of tree objects...
var client1 = new AdjacencyClient(1, 4)
{
first_name = "Pic'nic",
last_name = "Basket #1",
ssn = "123-45-6789"
};
var client2 = new AdjacencyClient(2, 4)
{
first_name = "Pic'nic",
last_name = "Basket #2",
ssn = "234-56-7890"
};
var client3 = new AdjacencyClient(3, 4)
{
first_name = "Bear",
last_name = "Cave",
ssn = "345-67-8901"
};
var client4 = new AdjacencyClient(4, 4)
{
first_name = "Picnic",
last_name = "Table",
ssn = "456-78-9012"
};
// build the next level up and add the children...
var officer1 = new AdjacencyUser(1, 3)
{
first_name = "Yogi",
last_name = "Bear",
officer_number = "YB123"
};
officer1.AddChild(client1);
officer1.AddChild(client2);
var officer2 = new AdjacencyUser(2, 3)
{
first_name = "Park",
last_name = "Ranger",
officer_number = "PR123"
};
officer2.AddChild(client3);
officer2.AddChild(client4);
// build the top of the tree and add the middle children...
var agencyThatAlreadyExists = new AdjacencyAgency(1, 2)
{
agency_name = "Jellystone",
agency_type = "Park",
};
agencyThatAlreadyExists.AddChild(officer1);
agencyThatAlreadyExists.AddChild(officer2);
return new List<AdjacencyTree>() { agencyThatAlreadyExists };
}
I guess I'm not totally sure what you're hoping to accomplish, but I can tell you this much.
First off, C# doesn't offer a good way to do exactly what I understand you're looking for. You won't be able to replicate what you've got using generics, because you can't inherit from a generic type argument (which makes sense, from a conflict-avoidance perspective).
That said, what I would do, is something like this:
public class AdjacencyTree<T> : AdjacencyTree
{
public AdjacencyTree(int entityId, int entityTypeId) : base(entityId, entityTypeId) { }
public T Value { get; set; }
}
Of course, you're adding an extra layer of properties here. You could call that a good or a bad thing, but that's the best there is.
You'll, of course, then need to instantiate them like this:
var client1 = new AdjacencyTree<Client>(1, 4)
{
Value = new Client()
{
first_name = "Pic'nic",
last_name = "Basket #1",
ssn = "123-45-6789"
}
};
It's up to you whether this is better or worse, but it does have the distinct advantage that you can play with Client instances without touching the collection type.
If you want to be extra clever, you could add a method like this:
public class AdjacencyTree<T> : AdjacencyTree
{
// ...
public void AddChild<TChild>(int entityId, int entityTypeId, TChild child)
{
var child = new AdjacencyTree<TChild>(entityId, entityTypeId)
{
Value = child
};
this.AddChild(child);
}
}
But that's really up to whether it would be useful. Some might object to adding this, as it unnecessarily duplicates the constructor arguments. But that's, of course, up to you. This is just another example.
Beyond that, I'd be tempted to drop entityTypeId from everything, unless you absolutely need it. That's up to how you want to use it, but I left it here based on your implementation. It seems like you could infer it based on T, but I wasn't sure why you needed it in your implementation either.
Even without generics being included, by my understanding, your existing pass-through constructor could look like this:
public AdjacencyUser(int entityId) : base(entityId, 3)
Thereby removing some redundancy. Of course, this is dependent on the assumption that two "User" types will always have the same EntityTypeId, which may or may not be fair.
I'm not sure if I've actually answered your question here, but hopefully it leads you in a bit of the right direction!

Categories

Resources