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.