Entity Framework Change Tracking And Automapper - entity-framework-6

Entity Framework classes:
namespace ORM
{
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public int Price { get; set; }
public virtual List<Wheel> Wheels { get; set; }
}
public class Wheel
{
public int Id { get; set; }
public double Size { get; set; }
public virtual Car Car { get; set; }
}
}
namespace DAL.Entities
{
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public int Price { get; set; }
public List<Wheel> Wheels { get; set; }
}
public class Wheel
{
public int Id { get; set; }
public double Size { get; set; }
public Car Car { get; set; }
}
}
ORM and DAL models.
Automapper configuration is:
public class DalProfile : Profile
{
public DalProfile()
{
CreateMap<Wheel, Entities.Wheel>()
.ForMember(m => m.Car, opt => opt.Ignore());
CreateMap<Entities.Wheel, Wheel>()
.ForMember(m => m.Car, opt => opt.Ignore());
CreateMap<Car, Entities.Car>()
.AfterMap((src, dest) =>
{
foreach (var wheel in dest.Wheels)
{
wheel.Car = dest;
}
});
CreateMap<Entities.Car, Car>()
.AfterMap((src, dest) =>
{
foreach (var wheel in dest.Wheels)
{
wheel.Car = dest;
}
});
CreateMap<Wheel, Wheel>()
.ForMember(m => m.Car, opt => opt.Ignore());
CreateMap<Car, Car>()
.AfterMap((src, dest) =>
{
foreach (var wheel in dest.Wheels)
{
wheel.Car = dest;
}
});
}
}
Repository class:
public class CarRepository
{
private readonly DbContext context;
public CarRepository(DbContext context)
{
this.context = context;
}
public void Update<T>(int id, T newCar) where T : class
{
var entity = context.Set<T>().Find(id);
Mapper.Map(newCar, entity);
context.SaveChanges();
}
}
Main entry:
static void Main(string[] args)
{
Mapper.Initialize(cfg => cfg.AddProfile(new DalProfile()));
DataContext context = new DataContext();
CarRepository carRepository = new CarRepository(context);
Car car = carRepository.Get(120);
DAL.Entities.Car dalCar = Mapper.Map<Car, DAL.Entities.Car(car);
dalCar.Name = "ew";
dalCar.Wheels[0].Size = 1994;
Car ormCar = Mapper.Map<DAL.Entities.Car, Car>(dalCar);
carRepository.Update(ormCar.Id, ormCar);
}
In my project i have ORM, DAL layers. Inside Update method i want to update only changed values. Of course if do changes with ORM.Car directly like this:
public void Update<T>(ORM.Car car) where T : class
{
var entity = context.Set<Car>().Find(car.id);
entity.Name = "New name";
entity.Wheels[0].Size = 1111;
context.SaveChanges();
}
This works great. Entity framework can update only Name property and related Wheel object.
But in my project i have different layers. So i want to change some properties of DAL.Car than map this object to ORM.Car with Automapper and apply changes like i did with ORM.Car above. But after mapping with Automapper i can't do this cause of Automapper creates new objects after mapping and Entity Framework can't update only needed properties like with ORM.Car directly cause of Dynamic Proxies maybe or i don't know. I want generic Update which looks something like this:
public void Update<T>(int id, T newCar) where T : class
{
var entity = context.Set<T>().Find(id);
Mapper.Map(newCar, entity);
context.SaveChanges();
}
Where newCar is a Car which is converted from DAL.Car;
Can i do this?

Related

Asp mvc 5 relationship many-to-many

How to display all of the materials in the table Material on the page, as well as a number of related data from the Teacher table?
How do I best remake?
Here I describe the models
Models:
public class Teacher
{
public int TeacherId { get; set; }
public string TeacherName { get; set; }
public virtual ICollection<Material> Materials { get; set; }
public Teacher()
{
Materials = new List<Material>();
}
}
public class Material
{
public int MaterialId { get; set; }
public string MaterialName { get; set; }
public string MaterialDescription { get; set; }
public virtual ICollection<Teacher> Teachers { get; set; }
public Material()
{
Teachers = new List<Teacher>();
}
}
The connection to the database
EfdbContext
public class EfDbContext : DbContext
{
// For Science Controller
public DbSet<Teacher> Teachers { get; set; }
public DbSet<Material> Materials { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Teacher>().HasMany(c => c.Materials)
.WithMany(s => s.Teachers)
.Map(t => t.MapLeftKey("TeacherId")
.MapRightKey("MaterialId")
.ToTable("TeacherMaterials"));
}
}
Controller:
public class ScienceController : Controller
{
EfDbContext context = new EfDbContext();
// GET: Science
public ActionResult ScienceResult()
{
IQueryable<Material> materials = context.Materials.Include(p => p.Teachers);
IQueryable<Teacher> teachers = context.Teachers.Include(p => p.Materials);
MateriaLTeacherListView materiaLTeacher = new MateriaLTeacherListView()
{
Materials = materials.ToList(),
Teachers = teachers.ToList()
};
return View(materiaLTeacher);
}
}
View:
#using Diploma.Domain.Entities
#model Diploma.WebUI.Models.MateriaLTeacherListView
#{
ViewBag.Title = "ScienceResult";
}
<div>
#foreach (var p in Model.Materials)
{
<p>#p.MaterialDescription</p>
foreach (Teacher t in Model.Teachers)
{
<li>#t.TeacherName</li>
}
}
</div>
this might work:
#foreach (var p in Model.Materials)
{
<p>#p.MaterialDescription</p>
foreach (Teacher t in p.Teachers)
{
<li>#t.TeacherName</li>
}
}

Delete statement not working properly?

My application is based upon code first entity framework.
I have model called module
public class Module
{
public long Id { get; set; }
public long ModuleTypeId { get; set; }
public ModuleType ModuleType { get; set; }
public string ModuleId { get; set; }
public PropertyConfiguration PropertyConfiguration { get; set; }
public DateTime DateEntered { get; set; }
}
And another Model class called OwnedModule
public class OwnedModule
{
public long Id { get; set; }
public PropertyConfiguration PropertyConfiguration { get; set; }
public long ModuleTypeId { get; set; }
public ModuleType ModuleType { get; set; }
public string ModuleId { get; set; }
public Guid ModuleOwnerId { get; set; }
public Module Module { get; set; }
public DateTime Start { get; set; }
public DateTime? End { get; set; }
}
The idea is that when u want to add new module into system u just add Module class, and OwnedModule is something that has a owner or is owned my someone.
So, basically OwnedModule contains Module and other properties. Here is the OwnedMOduleMap class
public class OwnedModuleMap : EntityTypeConfiguration<OwnedModule>
{
public OwnedModuleMap()
{
Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasKey(x => x.Id);
HasOptional(x => x.PropertyConfiguration).WithOptionalPrincipal().WillCascadeOnDelete();
HasRequired(x => x.ModuleType).WithMany().HasForeignKey(x => x.ModuleTypeId);
HasOptional(x => x.Module).WithOptionalDependent();
}
}
If I had to delete Module I would first have to delete it from OwnedModules table and then delete it from Modules table. IF it has a owner that is.
In my repository this is what I've done:
public void DeleteModule(long id)
{
var module = _dbSis.Modules.FirstOrDefault(t=>t.Id==id);
DeleteOwnedModule(module.ModuleId);
_dbSis.Entry(module).State = EntityState.Deleted;
_dbSis.SaveChanges();
}
public long GetOwnedModuleId(string moduleId)
{
var ownedModule= _dbSis.OwnedModules.FirstOrDefault(t => t.ModuleId == moduleId);
if (ownedModule != null)
{
return ownedModule.Id;
}
return 0;
}
public void DeleteOwnedModule(string moduleId)
{
var ownedModuleId = GetOwnedModuleId(moduleId);
var ownedModule = new OwnedModule
{
Id = ownedModuleId
};
_dbSis.Entry(ownedModule).State = EntityState.Deleted;
_dbSis.SaveChanges();
}
But right now my DeleteOwnedModule method is not working, I get error saying, why is this happening? What do i need to make my Delete statement work?
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
PS: if i were not to set the owner while adding a Module, then there is no problem while deleting it.
public void DeleteModule(long id)
{
var module = _dbSis.Modules.FirstOrDefault(t=>t.Id==id);
//DeleteOwnedModule(module.ModuleId);
_dbSis.Entry(module).State = EntityState.Deleted;
_dbSis.SaveChanges();
}
this works perfectly, if I were to delete it manually, then I can open database delete the module I want from OwnedModules table first then from Modules table. This works too but deleting from the method in repopsitory is not working
The problem is this method:
public void DeleteOwnedModule(string moduleId)
{
var ownedModuleId = GetOwnedModuleId(moduleId);
var ownedModule = new OwnedModule
{
Id = ownedModuleId
};
_dbSis.Entry(ownedModule).State = EntityState.Deleted;
_dbSis.SaveChanges();
}
You are creating a new OwnedModule with the id of an existing one. What you need to do is Find the existing module and set that instances state to Deleted (as you do for Module in the method DeleteModule)
public void DeleteOwnedModule(long id)
{
var ownedModule = _dbSis.OwnedModules.FirstOrDefault(t=>t.Id==id);
_dbSis.Entry(ownedModule).State = EntityState.Deleted;
_dbSis.SaveChanges();
}
Incidentally you have a lot of calls to SaveChanges(). Once you get all your code working you may want to consider reducing the number of calls to SaveChanges().

NavigationProperty has wrong data

I have a very simple example I am trying to set up with the following schema...
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public string SomethingVeryBig { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
public int Id { get; set; }
public int FooId { get; set; }
public string Name { get; set; }
}
What I am trying to test is using Breeze in a way disconnected from my data repository, so I am hand coding from the Fluent API my DBContext. Context code below, "FoosDb" is just an sdf file deployed with the project for the Breeze Metadata and is not a real database we are saving data into.
public class FoosDbContext : DbContext
{
public FoosDbContext() : base(nameOrConnectionString: "FoosDb")
{
Database.SetInitializer<FoosDbContext>(null);
}
public DbSet<Foo> Foos { get; set; }
public DbSet<Bar> Bars { get; set; }
public DbSet<Link> Links { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Foo>().HasKey(f => f.Id);
modelBuilder.Entity<Bar>().HasKey(b => b.Id);
modelBuilder.Entity<Foo>().HasMany(f => f.Bars).WithRequired().HasForeignKey(b => b.FooId);
}
}
public class FoosContextProvider : EFContextProvider<FoosDbContext>
{
public FoosContextProvider() : base() { }
protected override List<KeyMapping> SaveChangesCore(Dictionary<Type, List<EntityInfo>> saveMap)
{
return new List<KeyMapping>();
}
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
return true;
}
protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
{
// return a map of those entities we want saved.
return saveMap;
}
}
Everything works great and I am testing all CRUD operations via a project from the Hot Towel Template, but when I query Foos from my controller the json data looks perfect, but when it gets transferred to Breeze/Knockout Observables the data in each "Foo.Bars" list is wrong. It is taking Bar.Id = 1 and always putting that on Foo.Id = 1, Bar.Id = 2 and putting that on Foo.Id = 2, and so on. Even though in my example Bar.Id = 2 should be on Foo.Id = 1.

How to reuse projections in Entity Framework?

I have an ASP.NET MVC application which uses Entity Framework to get data.
I need to transform Entites to Models before passing them to View. Projections can be very complex, but to keep it simple:
public static IQueryable<UserModel> ToModel(this IQueryable<User> users)
{
return from user in users
select new UserModel
{
Name = user.Name,
Email = user.Email,
};
}
This can be used in a controller like this:
return View(Repository.Users.ToModel().ToList());
Very good. But what if I want to use this projection inside another one? Example:
public static IQueryable<BlogPostModel> ToModel(this IQueryable<BlogPost> blogs)
{
return from blogs in blogs
select new BlogPostModel
{
Title = blog.Title,
Authors = blog.Authors.AsQueryable().ToModel(), // (entities are POCOs)
// This does not work, because EF does not understand method ToModel().
};
}
(let's suppose blog can have more then one author and it is of type User).
Can I somehow separate the projections and reuse them inside another ones?
Here's something that actually works (in a simple test application) to only select the requested fields:
namespace Entities
{
public class BlogPost
{
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual DateTime Created { get; set; }
public virtual ICollection<User> Authors { get; set; }
}
public class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Email { get; set; }
public virtual byte[] Password { get; set; }
public virtual ICollection<BlogPost> BlogPosts { get; set; }
}
}
namespace Models
{
public class BlogPostModel
{
public string Title { get; set; }
public IEnumerable<UserModel> Authors { get; set; }
}
public class UserModel
{
public string Name { get; set; }
public string Email { get; set; }
}
public static class BlogPostModelExtensions
{
public static readonly Expression<Func<BlogPost, BlogPostModel>> ToModelConverterExpression =
p =>
new BlogPostModel
{
Title = p.Title,
Authors = p.Authors.AsQueryable().Select(UserModelExtensions.ToModelConverterExpression),
};
public static readonly Func<BlogPost, BlogPostModel> ToModelConverterFunction = ToModelConverterExpression.Compile();
public static IQueryable<BlogPostModel> ToModel(this IQueryable<BlogPost> blogPosts)
{
return blogPosts.Select(ToModelConverterExpression);
}
public static IEnumerable<BlogPostModel> ToModel(this IEnumerable<BlogPost> blogPosts)
{
return blogPosts.Select(ToModelConverterFunction);
}
}
public static class UserModelExtensions
{
public static readonly Expression<Func<User, UserModel>> ToModelConverterExpression =
u =>
new UserModel
{
Name = u.Name,
Email = u.Email,
};
public static readonly Func<User, UserModel> ToModelConverterFunction = ToModelConverterExpression.Compile();
public static IQueryable<UserModel> ToModel(this IQueryable<User> users)
{
return users.Select(ToModelConverterExpression);
}
public static IEnumerable<UserModel> ToModel(this IEnumerable<User> users)
{
return users.Select(ToModelConverterFunction);
}
}
}
To test it without actually creating a database:
var blogPostsQuery = (
from p in context.BlogPosts
where p.Title.StartsWith("a")
select p).ToModel();
Console.WriteLine(((ObjectQuery)blogPostQuery).ToTraceString());

Can automapper map a foreign key to an object using a repository?

I'm trying out Entity Framework Code first CTP4. Suppose I have:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public Parent Mother { get; set; }
}
public class TestContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
}
public class ChildEdit
{
public int Id { get; set; }
public string Name { get; set; }
public int MotherId { get; set; }
}
Mapper.CreateMap<Child, ChildEdit>();
Mapping to the Edit model is not a problem. On my screen I select the mother through some control (dropdownlist, autocompleter, etc) and the Id of the mother gets posted in back:
[HttpPost]
public ActionResult Edit(ChildEdit posted)
{
var repo = new TestContext();
var mapped = Mapper.Map<ChildEdit, Child>(posted); // <------- ???????
}
How should I solve the last mapping? I don't want to put Mother_Id in the Child object. For now I use this solution, but I hope it can be solved in Automapper.
Mapper.CreateMap<ChildEdit, Child>()
.ForMember(i => i.Mother, opt => opt.Ignore());
var mapped = Mapper.Map<ChildEdit, Child>(posted);
mapped.Mother = repo.Parents.Find(posted.MotherId);
EDIT
This works, but now I have to do that for each foreign key (BTW: context would be injected in final solution):
Mapper.CreateMap<ChildEdit, Child>();
.ForMember(i => i.Mother,
opt => opt.MapFrom(o =>
new TestContext().Parents.Find(o.MotherId)
)
);
What I'd really like would be:
Mapper.CreateMap<int, Parent>()
.ForMember(i => i,
opt => opt.MapFrom(o => new TestContext().Parents.Find(o))
);
Mapper.CreateMap<ChildEdit, Child>();
Is that possible with Automapper?
First, I'll assume that you have a repository interface like IRepository<T>
Afterwards create the following class:
public class EntityConverter<T> : ITypeConverter<int, T>
{
private readonly IRepository<T> _repository;
public EntityConverter(IRepository<T> repository)
{
_repository = repository;
}
public T Convert(ResolutionContext context)
{
return _repository.Find(System.Convert.ToInt32(context.SourceValue));
}
}
Basically this class will be used to do all the conversion between an int and a domain entity. It uses the "Id" of the entity to load it from the Repository. The IRepository will be injected into the converter using an IoC container, but more and that later.
Let's configure the AutoMapper mapping using:
Mapper.CreateMap<int, Mother>().ConvertUsing<EntityConverter<Mother>>();
I suggest creating this "generic" mapping instead so that if you have other references to "Mother" on other classes they're mapped automatically without extra-effort.
Regarding the Dependency Injection for the IRepository, if you're using Castle Windsor, the AutoMapper configuration should also have:
IWindsorContainer container = CreateContainer();
Mapper.Initialize(map => map.ConstructServicesUsing(container.Resolve));
I've used this approach and it works quite well.
Here's how I did it: (using ValueInjecter)
I made the requirements a little bigger just to show how it works
[TestFixture]
public class JohnLandheer
{
[Test]
public void Test()
{
var child = new Child
{
Id = 1,
Name = "John",
Mother = new Parent { Id = 3 },
Father = new Parent { Id = 9 },
Brother = new Child { Id = 5 },
Sister = new Child { Id = 7 }
};
var childEdit = new ChildEdit();
childEdit.InjectFrom(child)
.InjectFrom<EntityToInt>(child);
Assert.AreEqual(1, childEdit.Id);
Assert.AreEqual("John", childEdit.Name);
Assert.AreEqual(3, childEdit.MotherId);
Assert.AreEqual(9, childEdit.FatherId);
Assert.AreEqual(5, childEdit.BrotherId);
Assert.AreEqual(7, childEdit.SisterId);
Assert.AreEqual(0, childEdit.Sister2Id);
var c = new Child();
c.InjectFrom(childEdit)
.InjectFrom<IntToEntity>(childEdit);
Assert.AreEqual(1, c.Id);
Assert.AreEqual("John", c.Name);
Assert.AreEqual(3, c.Mother.Id);
Assert.AreEqual(9, c.Father.Id);
Assert.AreEqual(5, c.Brother.Id);
Assert.AreEqual(7, c.Sister.Id);
Assert.AreEqual(null, c.Sister2);
}
public class Entity
{
public int Id { get; set; }
}
public class Parent : Entity
{
public string Name { get; set; }
}
public class Child : Entity
{
public string Name { get; set; }
public Parent Mother { get; set; }
public Parent Father { get; set; }
public Child Brother { get; set; }
public Child Sister { get; set; }
public Child Sister2 { get; set; }
}
public class ChildEdit
{
public int Id { get; set; }
public string Name { get; set; }
public int MotherId { get; set; }
public int FatherId { get; set; }
public int BrotherId { get; set; }
public int SisterId { get; set; }
public int Sister2Id { get; set; }
}
public class EntityToInt : LoopValueInjection
{
protected override bool TypesMatch(Type sourceType, Type targetType)
{
return sourceType.IsSubclassOf(typeof(Entity)) && targetType == typeof(int);
}
protected override string TargetPropName(string sourcePropName)
{
return sourcePropName + "Id";
}
protected override bool AllowSetValue(object value)
{
return value != null;
}
protected override object SetValue(object sourcePropertyValue)
{
return (sourcePropertyValue as Entity).Id;
}
}
public class IntToEntity : LoopValueInjection
{
protected override bool TypesMatch(Type sourceType, Type targetType)
{
return sourceType == typeof(int) && targetType.IsSubclassOf(typeof(Entity));
}
protected override string TargetPropName(string sourcePropName)
{
return sourcePropName.RemoveSuffix("Id");
}
protected override bool AllowSetValue(object value)
{
return (int)value > 0;
}
protected override object SetValue(object sourcePropertyValue)
{
// you could as well do repoType = IoC.Resolve(typeof(IRepo<>).MakeGenericType(TargetPropType))
var repoType = typeof (Repo<>).MakeGenericType(TargetPropType);
var repo = Activator.CreateInstance(repoType);
return repoType.GetMethod("Get").Invoke(repo, new[] {sourcePropertyValue});
}
}
class Repo<T> : IRepo<T> where T : Entity, new()
{
public T Get(int id)
{
return new T{Id = id};
}
}
private interface IRepo<T>
{
T Get(int id);
}
}
It's possible to define the foreign key in EF this way as well:
[ForeignKey("MotherId")]
public virtual Parent Mother { get; set; }
public int MotherId { get; set; }
In this case, It's not necessary to do an extra query to find the Mother. Just Assign the ViewModel's MotherId to the Model's MotherId.

Resources