I just started using Entity Framework and it created a Context class which I can use to get all the data i need from it. But I am facing an issue on how I should organize my code, by watching the demos, the person just uses the framework and codes everything on a console application. What is the best way to use Entity Framework and that it looks clean?, what I mean by this is...right now using aspx pages, I could just use the aspx.cs to get the data or save the data. But I do not want this, I would like it to be more organized although the Entity Framework did almost everything by creating the objects etc.. but still, I need to use things like
using(var myobject = new MyContextData())
{
blah blah..
}
would you say that it would be nicer to write classes that would wrap these calls?. I would really appreciate any inputs as it would really make me a better programmer using the entity framework.
Regards
This question should everyone, who provides some tutorial about EF, ask. It is hard to say what is the best way, but put all code in the codebehind classes (aspx.cs) does not help extensibility and testability. Please, try to read this article:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Not only it is official tutorial on asp.net, but it mostly shows, that Framework EF could be used correctly in currently fancy Repository pattern
Edit:
I think that Generic Repository is Anti Pattern. But I do not understand #TomTom comment.
Original Answer:
As Radim Köhler mentioned you need to implement Repository and Unit of Work patterns
But the article he provided in my opinion is not fully correct.
At my current job I use following implementation of these patterns.
For example, we have three types of entities: Person, Good and Order. I created repository for Persons. In common case Repository must not be generic. It must contain methods which represent specific queries for this entity. So by looking at the interface of repository you can tell what kinds of queries executed for entity (Person, e.g.). As you will see I created DTO for Person called PersonWrap. For creating PersonWrap from Person and updating Person from PersonWrap you can use AutoMapper instead of PersonWrap() constructor and Update() method. Because EntityFramework DbContext implements Unit of Work pattern, you just need to provide created DbContext to repository methods. If repository method is a separate action and you do not need DbContext outside of this method you can create and dispose it inside this method.
public class Person {
public int Id { get; set; }
public string FirstName { get; set; }
public string SecondName { get; set; }
public DateTime RegistrationDate { get; set; }
public List<Order> Orders { get; set; }
}
public class Good {
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class Order {
public int Id { get; set; }
public Person Person { get; set; }
public Good Good { get; set; }
public int Count { get; set; }
}
public class MyDbContext: DbContext
{
public IDbSet<Person> Persons { get { return Set<Person>(); }}
public IDbSet<Good> Goods { get { return Set<Good>(); }}
public IDbSet<Order> Orders { get { return Set<Order>(); }}
}
public class PersonRepository {
public IEnumerable<Person> GetAll() {
using (var context = new MyDbContext()) {
return context.Persons.ToList();
}
}
public IEnumerable<Person> GetLastWeekPersons() {
using (var context = new MyDbContext()) {
return context.Persons.Where(p => p.RegistrationDate > new DateTime().AddDays(-7)).ToList();
}
}
public Person GetById(int id, MyDbContext context) {
return context.Persons.Include(p => p.Orders).FirstOrDefault(p => p.Id == id);
}
public Person GetById(int id) {
using (var context = new MyDbContext()) {
return GetById(id, context);
}
}
}
public class PersonWrap {
public int Id { get; set; }
public string FirstName { get; set; }
public string SecondName { get; set; }
public int OrderCount { get; set; }
public PersonWrap(Person person) {
Id = person.Id;
FirstName = person.FirstName;
SecondName = person.SecondName;
OrderCount = person.Orders.Count;
}
public void Update(Person person) {
person.FirstName = FirstName;
person.SecondName = SecondName;
}
}
public class PersonDetailsViewController {
public PersonWrap Person { get; protected set; }
public PersonDetailsViewController(int personId) {
var person = new PersonRepository().GetById(personId);
if (person != null) {
Person = new PersonWrap(person);
}
}
public void Save() {
using (var context = new MyDbContext()) {
var person = new PersonRepository().GetById(Person.Id, context);
Person.Update(person);
context.SaveChanges();
}
}
}
You are on the right track for creating classes to handle your EF.
The biggest benefit for doing it this way is able to unit test easily.
Test early and test often is always a good idea.
I suggest putting your EF related classes in a separate project.
Related
I have a MySql database with columns Id int and Name:json
Places Table Sample
Id Name
1 {"en":"Sphinx","ar":"أبو الهول","fr":"Le sphinx"}
C# Place class
public class Place
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
I'm connecting with EntityFramework 6 and connection success and retrieve data like this
{Id = 1, Name = "{\"en\":\"Sphinx\", \"ar\":\"أبو الهول\", \"fr\":\"Le sphinx\"}" }
What I want how to Map Name to new Object not JSON string
something like this
Place class
public class Place
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public Localized<string> Name { get; set; }
}
Localized class
public class Localized<T>
{
public T en { get; set; } // english localization
public T ar { get; set; } // arabic localization
public T fr { get; set; } // french localization
}
when I do this Name property come with NULL value
Code in Repository
using (var context = new PlacesEntityModel())
{
return context.Places.Take(5).ToList();
}
I don't want to use AutoMapper,
I want something in EntityFramework to select only one language in Database Level without fetching all other data and then map it
how to fix this?
You can try extension method to map from your entity type.
public class Place
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
public class PlaceDTO
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public Localized<string> Name { get; set; }
}
public class Localized<T>
{
public T en { get; set; } // english localization
public T ar { get; set; } // arabic localization
public T fr { get; set; } // french localization
}
Extenstion Method ToDto
public static class Extensions
{
public static PlaceDTO ToDto(this Place place)
{
if (place != null)
{
return new PlaceDTO
{
Id = place.Id,
Name = JsonConvert.DeserializeObject<Localized<string>>(place.Name)
};
}
return null;
}
}
Usage
var place = new Place() { Id = 1, Name = "{\"en\":\"Sphinx\", \"ar\":\"أبو الهول\", \"fr\":\"Le sphinx\"}" };
var placeDTO = place.ToDto();
Console.WriteLine($"{placeDTO.Id}-{placeDTO.Name.ar}-{placeDTO.Name.en}-{placeDTO.Name.fr}");
First of all, by using a class with a property per language, you restrict yourself. You'd always have to add new properties if you add new languages, which would of course be feasible, but unnecessary complicated. Furthermore you'd usually have the language as a string-ish object (or be able to convert), hence this would lead to code like this
Localized<string> name = ...;
switch(language)
{
case "en":
return name.en;
case "ar":
return name.ar;
case "fr":
return name.fr;
default:
throw new LocalizationException();
}
which is error-prone and overly complicated. For your problem, I think I'd opt to use some kind of dictionary
IDictionary<string, string> names = ...;
if(names.ContainsKey(language))
{
return names[language];
}
else
{
throw new LocalizationException();
}
which is easily extensible by just adding more translations to the dictionary.
To convert your JSON string to an IDcitionary<string, string>, you could use the following code
localizedNames = JObject.Parse(Name)
.Children()
.OfType<JProperty>()
.ToDictionary(property => property.Name,
property => property.Value.ToString());
From within your class this would effectively be
public class Place
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
public Dictionary<string, string> LocalizedNames
{
get
{
return JObject.Parse(Name)
.Children()
.OfType<JProperty>()
.ToDictionary(property => property.Name,
property => property.Value.ToString());
}
}
}
The localized values can be accessed like
var localizedPlaceName = place.LocalizedNames[language];
Please note: Depending on your needs and use cases, you should consider the following issues:
Caching
In my snippet, the JSON string is parsed every time the localized names are accessed. Depending on how often you access it, this might be detrimental to performance, which could be mitigated by caching the result (don't forget to delete the cache when Name is set).
Separation of concerns
The class as is is supposed to be a pure model class. You might want to introduce domain classes that encapsulate the presented logic, rather than adding the logic to the model class. Having a factory that creates readily localized objects based on the localizable object and the language could be an option, too.
Error handling
In my code there is no error handling. Depending on the reliability of input you should consider additional error handling.
devart.com/dotconnect/mysql/docs/EF-JSON-Support.html
Like what #Nkosi said
In that case then, take a look at this article devart.com/dotconnect/mysql/docs/EF-JSON-Support.html
It probably can given that the library was able to build that feature in. You would need to figure out what they they did (reverse engineer)
I usually just use JSON.Net, I notice that another answer referenced JObject, but without going into whether your data-model is the right model, I generally find that you can do:
var MyObjectInstance = JObject.Parse(myJsonString).ToObject<MyObjectType>();
I notice that you have ComponentModel attributes on your class. I don't know off hand how many of these JSon.Net supports, and you'd have to research that. It definitely supports some attributes from XML serialization, and also has some of it's own.
Note that you can also convert a JSOn array into a list:
var MyObjectList = JArray.Parse(myJsonString).ToObject<IEnumerable<MyObjectType>();
I want something in EntityFramework to select only one language in
Database Level without fetching all other data and then map it
if you want it to be from database level, you can always create a view and then include this view in your project.
Example :
CREATE VIEW `PlacesLocalized` AS
SELECT
Id
, TRIM(REPLACE(name->'$.en', '"','')) AS en
, TRIM(REPLACE(name->'$.ar', '"','')) AS ar
, TRIM(REPLACE(name->'$.fr', '"','')) AS fr
FROM
places
This would create a model class Like :
public class PlacesLocalized
{
public int Id { get; set; }
public string en {get; set;}
public string ar {get; set;}
public string fr {get; set;}
}
Then, you can do :
var places = context.PlacesLocalized.Where(x=> x.en == "Sphinx");
But if you don't have enough permissions to do this in the database level, then you would need to specify the query in your EF. There is no easy way to change the execution logic of Entity Framework just for specific classes. That's why Entity Framework included SqlQuery method, which would give more flexibility to have custom queries when needed (like yours).
So, if you need to specify the localization from Entity Framework, then you would do a repository class to specify all custom queries you need including creating any DTO needed.
The basic way would be something like this :
public enum Localized
{
English,
Arabic,
French
}
public class PlaceRepo : IDisposable
{
private readonly PlacesEntityModel _context = new PlacesEntityModel();
public List<Place> GetPlacesLocalized(Localized localized = Localized.English)
{
string local = localized == Localized.Arabic ? "$.ar"
: localized == Localized.French ? "$.fr"
: "$.en";
return _context.Places.SqlQuery("SELECT Id, name-> #p0 as Name FROM places", new[] { local })
.Select(x=> new Place { Id = x.Id, Name = x.Name.Replace("\"", string.Empty).Trim() })
.ToList();
}
private bool _disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_context.Dispose();
}
_disposed = true;
}
}
~PlaceRepo()
{
Dispose(false);
}
}
now, you can do this :
using(var repo = new PlaceRepo())
{
var places = repo.GetPlacesLocalized(Localized.Arabic);
}
public class Place
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
public static explicit operator Place(PlaceDTO dto)
{
return new Place()
{
Id = dto.Id,
Name = dto.Name
};
}
}
public class PlaceDTO
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public Localized<string> Name { get; set; }
public static explicit operator PlaceDTO(Place pls)
{
return new PlaceDTO()
{
Id = pls.Id,
Name = pls.Name
};
}
}
var placeDTO = (placeDto)place;
we can achieve this using explicit operator without using auto mapper
So, I have this User Entity
using System;
using System.Collections.Generic;
using System.Text;
using Transport.Data.Entities;
namespace Transport.Data.Entities
{
public class User : BaseEntity
{
public String FirstName { get; set; }
public String LastName { get; set; }
public DateTime BirthDay { get; set; }
public String Email { get; set; }
public String UserName { get; set; }
public bool IsActive { get; set; }
public List<Viaje> Viaje { get; set; }
}
}
and here is the ViewModel for the Entity
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;
using Transport.Data.Entities;
using Transport.Model.Infraestructure;
namespace Transport.Model.ViewModel
{
public class ViajeViewModel : BaseViewModel
{
public string Route { get; set; }
public string Destination { get; set; }
public string Origin { get; set; }
public int Price { get; set; }
public DateTime DepartureTime { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public Viaje Viaje { get; set; }
public List<User> User { get; set; }
}
}
This is my Update Repository
DataResult IRepository<T>.Update(T entity)
{
DataResult result = new DataResult();
try
{
result.Data = entity;
context.SaveChanges();
result.Successfull = true;
}
catch (Exception ex)
{
result.LogError(ex);
result.Successfull = false;
}
return result;
}
And my update service
public ServiceResult Update(Vm viewModel)
{
ServiceResult serviceResult = new ServiceResult();
var ToUpdate = this.Repository.GetById((int)viewModel.Id).Data;
if (ToUpdate == null)
{
serviceResult.Success = false;
serviceResult.ResultTitle = "ERROR: Record No Found";
//serviceResult.Messages.Add(Error.GetErrorMessage(Error.RecordNotFound));
return serviceResult;
}
var Entity = MapperHelper.Instance.Map<Vm, Ent>(viewModel);
var result = this.Repository.Update(Entity);
serviceResult.Success = result.Successfull;
serviceResult.ResultTitle = (result.Successfull ? Error.GetErrorMessage(Error.CorrectTransaction) : Error.GetErrorMessage(Error.InternalServerError));
//serviceResult.Messages.Add(result.Successfull ? "Updated" : "Failed");
serviceResult.ResultObject = MapperHelper.
Instance.Map<Ent, Vm>(result.Data);
this.Repository.SaveChanges();
return serviceResult;
}
This is my update user controller
[HttpPost("users/edit/{id}")]
public ActionResult UserEdit(UserViewModel userViewModel)
{
var users = userService.Update(userViewModel).ResultObject;
return RedirectToAction("Index", "Users");
}
The repository and service are doing their jobs looking for an user by an id and updating their values but when the UserEdit Controller it's done my changes are not being saved in the database.
Can anyone give me some advice to resolve this problem ?
EF relies on internal entity change tracking to determine what operations it needs to perform in the database. All your Update method does is call SaveChanges, so simply changes being made to the entity are not being tracked for some reason, and when you call SaveChanges, EF sees no work it needs to do, and just returns. As to why your entity changes aren't being tracked, there's not enough of your repository here to tell.
However, I will say that this is one of the paramount reasons to not use the repository pattern with EF. It's far too easy to do stuff that borks EF's change tracking, and 99 times out of 100, that's exactly what developers do. When you use an ORM like EF, that is your data layer. It implements the repository and unit of work patterns already. Not every "layer" in your architecture has to actually be owned by you, and this is a critical mistake far too many developers make. Just use your context directly. That's what it's for.
Now, purists might argue that you'll have a hard dependency on EF. Well, guess what? You do regardless. You've chosen it as an ORM, and that decision doesn't and shouldn't come lightly. What if you want to switch it out with something else down the line? That question always gets raised as well. Simply, you won't. The friction involved in switching out something like an ORM is such that it will never be a business priority.
Nevertheless, if you want to truly abstract the dependency, you should be looking at patterns like CQRS or microservices, which unlike a redundant and useless repository layer, actually do add value to your application. However, those patterns are complex to implement and overkill for most applications.
I recently did some work on adjusting the Dapper to my needs and it works quite good but I noticed I have certain code that repeates and I want to minify that as much as possible. Current way this works is:
I have a base DAL class that does some basic operation such as Save, Delete, Get, etc. Along with that it has a property named IsNew that...well...you've guessed determines if it's new or existing entity.
public class DalBase
{
public bool IsNew
{
get;
private set;
}
public static T New<T>()
{
// removed for clarity
}
public void Get<T>(params object[] parameters)
{
// removed for clarity
}
public void Save()
{
// removed for clarity
}
public void Delete()
{
// removed for clarity
}
}
If I want to define my entity, lets say person, I would do it this way:
public partial class Person
{
private PersonDal m_dal;
public bool IsNew
{
get
{
return m_dal.IsNew;
}
}
public int? PersonID
{
get
{
return m_dal.PersonID;
}
}
public string PersonName
{
get
{
return m_dal.PersonName;
}
set
{
m_dal.PersonName = value;
}
}
public string PersonSurname
{
get
{
return m_dal.PersonSurname;
}
set
{
m_dal.PersonSurname = value;
}
}
public DateTime? PersonDateOfBirth
{
get
{
return m_dal.PersonDateOfBirth;
}
set
{
m_dal.PersonDateOfBirth = value;
}
}
private Person()
{
m_dal = DalBase.New<PersonDal>();
}
public static Person New()
{
return new Person();
}
public static Person Get(int PersonID)
{
var personDal = DalBase.Get<PersonDal>(PersonID);
if (personDal == null)
return null;
var person = new Person();
person.m_dal = personDal;
return person;
}
private class PersonDal : DalBase
{
public int? PersonID
{
get;
set;
}
public string PersonName
{
get;
set;
}
public string PersonSurname
{
get;
set;
}
public DateTime? PersonDateOfBirth
{
get;
set;
}
private PersonDal()
{
IsNew = true;
}
}
}
As you can see my DAL is contained inside the Entity class. This way I can do all sorts of things like mappings, preliminary checks before update or insert, etc. But, this is not why I copy pasted this code here :)
What I want to do is somehow eliminate IsNew from the entity because it will repeate on every entity definition and it seems a bit redundant AND more because I might be thinking of adding some columns that are common for all tables such as DateOfEdit, UserEdit, etc... As you can see it would be very handy to add it in one place and than inherit it and just keep specific columns in each entity definition.
What I tried doing by now is using generics like this:
public class EntityBase<T> where T : DalBase
{
protected T m_dal;
public bool IsNew
{
get
{
return m_dal.IsNew;
}
}
}
This would allow me to inherit base entity with specific Dal class so I can have my person entity defined as such:
public partial class Person : EntityBase<PersonDal>
The problem with this approach is that the PersonDal is private class of Person entity and as such has less accessibility than the entity itself and if I just define it as public - well it beats the whole purpose since the DAL is used only by the entity that is concerned with it!
Is there any other way that we could achieve this?
DAL has nothing to do with the entities. You mix the concepts here. Use DAL as an individual class, not as a member field/variable of your entity classes and pass the entity types to DAL.
If you have to have the DAL in your entities, use DI and pass the DAL instance in the constructor.
Or if you want to use your original approach, create a simple EF project with two entity types and check how it works.
Say we have two domain classes.
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
Now let's create a Context.
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
public BloggingContext () : base("Blogging")
{
Database.SetInitializer<BloggingContext >(new CreateDatabaseIfNotExists<BloggingContext>());
}
Also in this class, we want to add some test data to verify it.
public void AddBlog(params)
{
using (BloggingContext db = new BloggingContext ())
{
var t = new Blog { Name =name };
db.Blogs.Add(t);
try
{
db.SaveChanges();
return true;
}
Then to test it, we create a unit test project.
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
BloggingContext bloging= new BloggingContext ();
List<Post> post = new List<Post>();
Post objPost = new Post();
objPost.Post = "some";
objPost.OtherFields = "test";
// etc;
post.Add(objPost);
bloging.AddBlog("MyBlog",post);
To run the test, I found the code went to the constructor of BloggingContext class first, thus Database.SetInitializer... was executed.
Then when
using (BloggingContext db = new BloggingContext ())
It called the constructor again, I am not sure whether it is ok.
You can call the constructor as many times you like
Database.SetInitializer<BloggingContext >(new CreateDatabaseIfNotExists<BloggingContext>());
The CreateDatabaseIfNotExists just does a DB check that if it really exists or not, if not it does create it, else it skips the database creation part !
Name of the strategy does not really make sense.
Actually, what the CreateDatabaseIfNotExists strategy does is:
check if the database with the specified name exist or not, if not
it'll create a new database
if the database with the specified name already exists, and if
there's no tables/procs found in the db at all, it means the db is
in "fresh new" status, then the strategy will also generate the db
structure and insert seed data
If the database with the specified name already exists and there's
some table / procs in the db then it will do nothing
I have an entity class Person and its corresponding DTO class PersonDto.
public class Person: Entity
{
public virtual string Name { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
public virtual Sex Sex { get; set; }
public virtual Position Position { get; set; }
public virtual Division Division { get; set; }
public virtual Organization Organization { get; set; }
}
public class PersonDto: Dto
{
public string Name { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public Guid SexId { get; set; }
public Guid PositionId { get; set; }
public Guid DivisionId { get; set; }
public Guid OrganizationId { get; set; }
}
After receiving a DTO object I have to convert it into a person entity. Now I do it completely manually. The code looks like this.
public class PersonEntityMapper: IEntityMapper<Person, PersonDto>
{
private IRepository<Person> _personRepository;
private IRepository<Sex> _sexRepository;
private IRepository<Position> _positionRepository;
private IRepository<Division> _divisionRepository;
private IRepository<Organization> _organizationRepository;
public PersonEntityMapper(IRepository<Person> personRepository,
IRepository<Sex> sexRepository,
IRepository<Position> positionRepository,
IRepository<Division> divisionRepository,
IRepository<Organization> organizationRepository)
{
... // Assigning repositories
}
Person Map(PersonDto dto)
{
Person person = CreateOrLoadPerson(dto);
person.Name = dto.Name;
person.Phone = dto.Phone;
person.Email = dto.Email;
person.Sex = _sexRepository.LoadById(dto.SexId);
person.Position = _positionRepository.LoadById(dto.PositionId);
person.Division = _divisionRepository.LoadById(dto.DivisionId);
person.Organization = _organizationRepository.LoadById(dto.OrganizationId);
return person;
}
}
The code is in fact trivial. But as the number of entities grows so does the number of mapper classes. The result is lots of similar code. Another issue is that when there are mode associations I have to add constructor parameteres for additional repositories. I tried to inject a some kind of a repository factory instead, but it smelled a bad-known Service Locator so I reverted to an original solution.
Unit testing of these mappers also results in a number of similar-looking test methods.
With all this been said I wonder if there exists a solution that can reduce the amount of manually written code and make the unit testing easier.
Thanks in advance.
UPDATE
I'd accomplished the task with Value Injecter but then I realized that I could safely remove it and the rest would still work. Here is the resulting solution.
public abstract class BaseEntityMapper<TEntity, TDto> : IEntityMapper<TEntity, TDto>
where TEntity : Entity, new()
where TDto : BaseDto
{
private readonly IRepositoryFactory _repositoryFactory;
protected BaseEntityMapper(IRepositoryFactory repositoryFactory)
{
_repositoryFactory = repositoryFactory;
}
public TEntity Map(TDto dto)
{
TEntity entity = CreateOrLoadEntity(dto.State, dto.Id);
MapPrimitiveProperties(entity, dto);
MapNonPrimitiveProperties(entity, dto);
return entity;
}
protected abstract void MapNonPrimitiveProperties(TEntity entity, TDto dto);
protected void MapPrimitiveProperties<TTarget, TSource>(TTarget target, TSource source, string prefix = "")
{
var targetProperties = target.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name);
var sourceProperties = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name);
foreach (var targetProperty in targetProperties) {
foreach (var sourceProperty in sourceProperties) {
if (sourceProperty.Name != string.Format("{0}{1}", prefix, targetProperty.Name)) continue;
targetProperty.SetValue(target, sourceProperty.GetValue(source, null), null);
break;
}
}
}
protected void MapAssociation<TTarget, T>(TTarget target, Expression<Func<T>> expression, Guid id) where T : Entity
{
var repository = _repositoryFactory.Create<T>();
var propertyInfo = (PropertyInfo)((MemberExpression)expression.Body).Member;
propertyInfo.SetValue(target, repository.LoadById(id), null);
}
private TEntity CreateOrLoadEntity(DtoState dtoState, Guid entityId)
{
if (dtoState == DtoState.Created) return new TEntity();
if (dtoState == DtoState.Updated) {
return _repositoryFactory.Create<TEntity>().LoadById(entityId);
}
throw new BusinessException("Unknown DTO state");
}
}
Mapping of each entity is performed with a concrete class derived from BaseEntityMapper. The one for Person entities looks like this.
public class PersonEntityMapper: BaseEntityMapper<Person, PersonDto>
{
public PersonEntityMapper(IRepositoryFactory repositoryFactory) : base(repositoryFactory) {}
protected override void MapNonPrimitiveProperties(Person entity, PersonDto dto)
{
MapAssociation(entity, () => entity.Sex, dto.SexId);
MapAssociation(entity, () => entity.Position, dto.PositionId);
MapAssociation(entity, () => entity.Organization, dto.OrganizationId);
MapAssociation(entity, () => entity.Division, dto.DivisionId);
}
}
Explicitly calling MapAssociation protects against future properties renamings.
You can have a look on the two most used Object-Object mapper:
AutoMapper
AutoMapper is a simple little library built to solve a deceptively
complex problem - getting rid of code that mapped one object to
another. This type of code is rather dreary and boring to write, so
why not invent a tool to do it for us?
Value Injecter
ValueInjecter lets you define your own convention-based matching
algorithms (ValueInjections) in order to match up (inject) source
values to destination values.
There is a comparison article on SO: AutoMapper vs ValueInjecter
You can use GeDA for mapping any entity to a DTO object, it comes with either annotations or DSL support.
http://inspire-software.com/confluence/display/GeDA/FAQ
There are only basic examples on the wiki but jUnits of source code are full of useful examples
You can get it from sourceforge or google code manually or via maven dependency
Details are here: http://inspire-software.com/confluence/display/GeDA/GeDA+-+Generic+DTO+Assembler