How to eager load child entities using repository pattern - entity-framework-4

I have an entity named Tour which can have many Agents. I am able to add agents, but I cannot remove them.
// _repo is injected....
var tour = _repo.GetById(tourId);
tour.AddAgent(new Agent(tour.TourId));
When I attempt to call the Tour.RemoveAgent() method nothing is actually removed. I set a breakpoint inside the Tour.RemoveAgent() method I see that the _agents property has a count of 0.
tour.RemoveAgent(agentId); // This doesn't work because _agents is empty
Do I have to do something special for EF to populate the _agents property when I retrieve the Tour from my repository?
UPDATE: PROBLEM SOLVED (thanks to Paul's answer)
I decided to just create a Repository unique to each aggregate, that way it is easy to define exactly what needs to be included using the Include() function. This is an example where I inherit from the GenericRepository<T> class (which is also included at the bottom of this question).
public class TourRepository : GenericRepository<Tour>
{
public TourRepository(IDatabaseFactory databaseFactory) : base (databaseFactory)
{
}
public override Tour GetById(Guid id)
{
return dataContext.Tours
.Include(x => x.Agents)
.Single(x => x.TourId == id);
}
}
Tour Class
public partial class Tour
{
public Guid TourId { get; private set; }
protected virtual List<Agent> _agents { get; set; }
public Tour()
{
TourId = Guid.NewGuid();
_agents = new List<Agent>();
}
public void AddAgent(Agent agent)
{
_agents.Add(agent);
}
public void RemoveAgent(Guid agentId)
{
_agents.RemoveAll(a => a.AgentId == agentId);
}
}
Agent Class
public partial class Agent
{
public Guid AgentId { get; private set; }
public Guid TourId { get; private set; }
public Tour Tour { get; private set; }
public Agent(Guid tourId)
{
TourId = tourId;
AgentId = Guid.NewGuid();
}
}
OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// AGENTS ============================
modelBuilder.Entity<Agent>()
.HasKey(x => x.AgentId)
.Property(p => p.AgentId);
modelBuilder.Entity<Agent>()
.HasRequired(p => p.Tour)
.WithMany(t => t.Agents);
// TOURS =============================
modelBuilder.Entity<Tour>()
.HasKey(x => x.TourId)
.Property(x => x.TourId);
}
Repository Class
public class GenericRepository<T> : IRepository<T> where T : class {
private MyContext dataContext;
private readonly IDbSet<T> dbset;
public GenericRepository(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<T>();
}
protected IDatabaseFactory DatabaseFactory
{
get;
private set;
}
protected MyContext DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
// ... stuff removed for brevity ...
public T GetById(Guid id)
{
return dbset.Find(id);
}
}

Try making protected virtual List _agents { get; set; } public
public virtual List<Agent> _agents { get; set; }
You can also eager load by doing something like this:
_databaseContext.Tours.Include(x => x.Agents).Single(x => x.TourId == tourId)
you can read more here: http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx

Related

StudentDbContext is null using Asp.net Core Web api

am a beginner in ASP.NET Core. I am creating a Web API service. While I am fetching the data from the database, I had a problem. What is the error I got? I have successfully done the database migration part and created the database successfully.
StudentDbContext is null
StudentController
namespace webb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class StudentController : ControllerBase
{
private StudentDbContext studentDbContext;
public StudentController(StudentDbContext studentDbContext)
{
studentDbContext = studentDbContext;
}
// GET: api/<EmployeeController>
[HttpGet]
public IEnumerable<Student> Get()
{
// var studens = studentDbContext.Student;
return studentDbContext.Student;
}
}
}
Model
public class Student
{
public int id { get; set; }
public string stname { get; set; }
public string course { get; set; }
}
}
StudentDbContext
public class StudentDbContext : DbContext
{
public StudentDbContext(DbContextOptions<StudentDbContext> options) : base(options)
{
}
public DbSet<Student> Student { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=.;Initial Catalog=ams;Integrated Security=True; TrustServerCertificate = True");
}
}
IDataService
public interface IDataService<T>
{
Task<IEnumerable<T>> GetAll();
Task<T> Get(int id);
Task<T> Create(T entity);
Task<bool> Delete(T entity);
Task<T> Update(T entity);
}
}
I have successfully done the database migration part and created the
database successfully. StudentDbContext is null
Well, two mistake has been done. Your model has no primary key. So you will always get null data when there is no primary key set to your table column.
Therefore, your model should be as following:
Model:
public class Student
{
[Key]
public int id { get; set; }
public string stname { get; set; }
public string course { get; set; }
}
Controller:
Another misake is here studentDbContext.Student; this will not bring anything. You would be liking to fetch student list instead. So you should write studentDbContext.Student.ToList();. As following"
[HttpGet]
public IEnumerable<Student> Get()
{
// var studens = studentDbContext.Student;
return studentDbContext.Student.ToList();
}
Note: In addition, your constructor convension is not correct, it can be written as following:
[Route("api/[controller]")]
[ApiController]
public class StudentController : ControllerBase
{
private readonly StudentDbContext _studentDbContext;
public StudentController(ApplicationDbContext studentDbContext)
{
_studentDbContext = studentDbContext;
}
// GET: api/<EmployeeController>
[HttpGet]
public IEnumerable<Student> Get()
{
// var studens = studentDbContext.Student;
return _studentDbContext.Student.ToList();
}
}
Note: You can check more details on asp.net core web api official document here
Output:
For further details you can have a look on official document here.

Unable to retrieve data from database using repository pattern

I'm currently learning interfaces and repository pattern and have difficulties with it. I have domain class "VehicleMake"
public class VehicleMake
{
public VehicleMake()
{
}
[Key]
[Column(Order = 1)]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public virtual ICollection<VehicleModel> VehicleModels { get; set; }
}
I also have an Interface "IVehicleRepository"
public interface IVehicleRepository
{
IEnumerable<VehicleMake> All { get; }
}
and a class "VehicleRepository" which implements that interface
public class VehicleRepository : IVehicleRepository
{
private readonly VehicleDbContext _context;
public VehicleRepository(VehicleDbContext context)
{
_context = context;
}
public IEnumerable<VehicleMake> All => _context.VehicleMakes;
}
My DbContext class is following
public class VehicleDbContext : DbContext
{
public VehicleDbContext() : base("VehicleDbContext")
{
}
public DbSet<VehicleMake> VehicleMakes { get; set; }
public DbSet<VehicleModel> VehicleModels { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
And my "VehicleController" is following
public class VehicleController : Controller
{
private readonly IVehicleRepository _vehicleRepository;
public VehicleController()
{
}
public VehicleController(IVehicleRepository vehicleRepository)
{
_vehicleRepository = vehicleRepository;
}
// GET: VehicleMakes
public ActionResult Index()
{
return View(_vehicleRepository.All);
}
}
So the problem is that when I try in my controller to retrieve "Vehicle Makes", I get null reference exception and I don't why. Did I implemented this properly? If not, what is the proper way to do this?
You are getting NullReferenceException because it's calling your parameterless constructor where you don't have initialization of _vehicleRepository field. Your initialization is in constructor where you are passing IVehicleRepository object.
I believe, you don't have dependedency injection framework registered for correctly getting instance. So, you can simply initialize _vehicleRepository in default constructor :
public class VehicleController : Controller
{
private readonly IVehicleRepository _vehicleRepository;
public VehicleController()
{
_vehicleRepository = new VehicleRepository();
}
// GET: VehicleMakes
public ActionResult Index()
{
return View(_vehicleRepository.All);
}
}
Change your VehicleRepository to support default constructor in following way :
public class VehicleRepository : IVehicleRepository
{
private readonly VehicleDbContext _context;
public VehicleRepository() : this(new VehicleDbContext())
{
}
public VehicleRepository(VehicleDbContext context)
{
_context = context;
}
public IEnumerable<VehicleMake> All => _context.VehicleMakes;
}

Entity Framework Change Tracking And Automapper

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?

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.

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