Automapper and Entity Framework

I had two problems with Automapper and Entity Framework, and I would like to know if my solutions are the best.

Context:

I have an ObjectA that has an ObjectB list, which in turn has an ObjectC .

ObjectC is a list in a database, for example, of a country. I can change ObjectC to ObjectB , but I do not add, I want to add ObjectC .

I am using MVVM, and ObjectB is listed in datagrid. To select ObjectC , you can select comboboxes.

I would like to keep ObjectA and ObjectB at the same time, so I use Automapper and transaction.

  public void SaveObjectA(ObjectA p_ObjectA) { OpenTransaction(); var l_Provider = new DataProvider<DB.ObjectA>(Context); var l_ObjectA = l_Provider.FindById(p_ObjectA.ID); Mapper.Map(p_ObjectA, l_ObjectA); CloseTransaction(); } 

Entity Framework Classes:

 public partial class ObjectA { public ObjectA() { this.ObjectB = new HashSet<ObjectB>(); } public System.Guid ID { get; set; } public virtual ICollection<ObjectB> ObjectB { get; set; } } public partial class ObjectB { public System.Guid ID { get; set; } public System.Guid ObjectCID { get; set; } public virtual ObjectC ObjectC { get; set; } } public partial class ObjectC { public System.Guid ID { get; set; } public string Name { get; set; } } 

DTO Classes:

 public class ObjectA : ObjectBase { public ObjectA () { ObjectB = new Collection< ObjectB >(); } public virtual ICollection<ObjectB> ObjectB { get; set; } } public class ObjectB : ObjectBase { private ObjectC _ObjectC { get; set; } public virtual ObjectC ObjectC { get { return _ObjectC; } set { _ObjectC = value; } } } public class ObjectC : ObjectBase { public string Name { get; set; } } public abstract class ObjectBase { public Guid ID { get; set; } } 

1st problem : when saving ObjectB , Entity Framework tries to insert ObjectC . But ObjectC already exists. I do not need an insert, but an update.

My solution (see on the forums):

 Mapper.CreateMap<ObjectB, DB.ObjectB>() .ForMember(pro=>pro.ObjectC, opt=>opt.Ignore()); 

But I do not understand, because if I ignore ObjectC , ObjectC should not be updated. However, it works (i.e.: update ok and it does not try to add a row to the database, and Automapper / EF can find the ObjectC in the database and update the ObjectCID on ObjectB ...)

Note: it also works with solving the first point of my second problem.

Second problem : when I update, add or delete a row on my datagrid, I would like to save the changes to the database.

  • Update: same problem as first: row already exists.

     Mapper.CreateMap<ObjectB, DB.ObjectB>() .ConstructUsing(s => Context.Set<DB.ObjectB>().Find(s.ID)); 

    I think the solution is to attach the correct row from the database and then populate it.

    Therefore, I use ConstructUsing to find the string.

  • Add: Then the identifiers are zero and I cannot save a new line

     Mapper.CreateMap<ObjectB, DB.ObjectB>() .ConstructUsing(s => Context.Set<DB.ObjectB>().Find(s.ID) ?? Context.Set<DB.ObjectB>().Create()) 

    In the case where the string does not exist, I have to create (and attach) the object to the context.

  • Delete: a row deleted by datagrid is not deleted in the database:

     Mapper.CreateMap<ObjectA, DB.ObjectA>() .ConstructUsing(s => Context.Set<DB.ObjectA>().Create()) .AfterMap ( (bef, aft) => aft.ObjectB.ToList() .Where(x => !bef.ObjectB.Select(z=>z.ID).Contains(x.ID)) .ToList() .ForEach(ele => Context.ObjectB .Remove(Context.ObjectB.Find(ele.ID))) ); 

So this works, but I wonder how you would do it, an easier way.

+6
source share
1 answer

Well, before I delve into your questions, let me just play a major role by quoting something from Autommaper docs :

AutoMapper is currently targeting model projection scenarios to smooth complex object models for DTOs and other simple objects whose design is best suited for serialization, messaging, messaging, or just an anti-corruption layer between the domain and application level

However, if you read it carefully, automapper was designed to convert from complex objects to simpler ones or read between lines, from Entity Objects to Pocos, DTO, ViewModels, etc. Not the other way around. What for? just because it's too complicated. It is not, you can just have new ObjectA() .

Question 1: This is the expected behavior from the Entity Framework (and other major runes). EF handles Entity objects. If you specify that the ObjectC Property is a new object, EF will try to insert it. If you want to use the one that exists, you need to first load it from the context or just use the ObjectCId foreign key.

Question 2: This is really almost the same answer as question 1. EF is a design for working with entity objects. When you change them, EF tracks the changes, and then after calling SaveChanges() they are saved in the database.

Just a special note about the exception. EF does not cascade delete, like "cascade = all-delete-orphan" in NHibernate. There are several workarounds , but in general you will need to control the removal of the child when you want to delete all relationships.

I know that this is probably not the answer you were looking for, but you are working on the problems that arise because you are not using the tools correctly.

General speaking, working with EF is simple:

1 - Loading objects from the database

2 - change them

3 - apply the changes to the database by calling SaveChanges()

0
source

Source: https://habr.com/ru/post/956485/


All Articles