How to eager load (include) a private field collection with a public read only property - ef-core-2.0

System.NotSupportedException : Collection is read-only.
at System.ThrowHelper.ThrowNotSupportedException(ExceptionResource resource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrICollectionAccessor`3.Add(Object instance, Object value)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.AddToCollection(InternalEntityEntry entry, INavigation navigation, IClrCollectionAccessor collectionAccessor, Object value)
public class Student
{
public int Id {get; private set;}
private List<StudentProgress> _progresses;
protected Student()
{
_progresses = new List<StudentProgress>();
}
public IEnumerable<StudentProgress> Progresses =>_progresses.AsReadOnly();
}
public class StudentProgress
{
public int Id {get; private set;}
public int ProgressStatusId { get; private set; }
public int Year { get; private set; }
public Grade Grade { get; private set; }
public int CourseId { get; private set; }
public StudentProgress(Grade grade, int year, int courseId, int progress)
{
ProgressStatusId = progress;
Year = year;
Grade = grade;
CourseId = courseId;
}
}
public class StudentEntityTypeConfiguration : IEntityTypeConfiguration<Student>
{
public void Configure(EntityTypeBuilder<Student> studentConfiguration)
{
studentConfiguration.HasKey(x => x.Id);
studentConfiguration.Ignore(x => x.DomainEvents);
studentConfiguration.Metadata.FindNavigation(nameof(Student.Progresses))
.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
public class Context : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfiguration(new StudentEntityTypeConfiguration());
}
}
this is where I'm getting the error (Repository):
public async Task<Student> FindAsync(int identity)
{
var student = await _context.Set<Student>()
.Include(x=>x.Progresses) // this line is generating the error
.Where(b => b.Id == identity)
.SingleOrDefaultAsync();
return student;
}

I attempted to change the way you're getting _progresses in my code:
public class Student
{
public int Id {get; private set;}
private List<StudentProgress> _progresses;
protected Student()
{
_progresses = new List<StudentProgress>();
}
public IEnumerable<StudentProgress> Progresses =>_progresses; // changed from AsReadOnly()
}

Related

Join two tables (One to Many) using asp.net core web api

I am new at C# entity framework. I am trying to build an API, but stuck in retrieving data from relational table.
I have a Game table in MS SQL database, where GameId is the primary key. I have another table called GameCharacter, where GameCharacterId is the primary key.
In Game table GameCharacterId is foreign key. How i can get all GameCharacters on Get Games.
public class Game
{
public Game()
{
GameCharacters = new List<GameCharacter>();
}
public int GameID { get; set; }
public string Title { get; set; }
public string Platform { get; set; }
public string imgpath { get; set; }
public int ReleaseYear { get; set; }
public virtual ICollection< GameCharacter> GameCharacters { get; set; }
}
public class GameCharacter
{
[Key]
public Guid CharID { get; set; }
public string CharName { get; set; }
public string CharGame { get; set; }
public string charimgpath { get; set; }
[ForeignKey("Game")]
public int GameID { get; set; }
public virtual Game Game { get; set; }
}
public class GameController : Controller
{
private readonly GameApiDbconnect dbContext;
public GameController(GameApiDbconnect dbContext)
{
this.dbContext = dbContext;
}
[HttpGet]
public async Task<IActionResult> GetGames()
{
return Ok(await dbContext.Games.ToListAsync());
}
[HttpGet]
[Route("{GameID=guid}")]
public async Task<IActionResult> GetGame([FromRoute] Guid GameID)
{
var game = await dbContext.Games.FindAsync(GameID);
if (game == null)
{
return NotFound();
}
return Ok(game);
}
OutPut
Response body
{
"gameID": 1,
"title": "string",
"platform": "string",
"imgpath": "string",
"releaseYear": 0,
"gameCharacters": []
}
just use include
[HttpGet]
public async Task<IActionResult> GetGames()
{
var games = await dbContext.Games
.Include( g=> g.GameCharacters)
.ToListAsync();
return Ok(games);
}
[HttpGet]
public async Task<IActionResult> GetGames()
{
return Ok(from g in dbContext.Games
join c in dbContext.GameCharacters on g.GameID
equals c.GameID into Gcharacters
select new
{
GameID = g.GameID,
Title = g.Title,
Platform = g.Platform,
imgpath = g.imgpath,
ReleaseYear = g.ReleaseYear,
GameCharacters = Gcharacters.Select(gc => new {
CharID = gc.CharID, CharName = gc.CharName, CharGame =
gc.CharGame, charimgpath = gc.charimgpath }) }
) ;
}

Saving data many-to-many relationship

My question is: I have two tables room and reservation . They are connected many to many from table RoomReservation.
public class Room
{
public int Id { get; set; }
public double CostNight { get; set; }
public virtual ICollection<Reservation> Reservation { get; set; }
}
public class Reservation
{
public int Id { get; set; }
public string DateOfEntry { get; set; }
public int NumberOfNights { get; set; }
public virtual ICollection<Room> Room { get; set; }
}
On My DbContext I configurated this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Reservation>().HasMany(c => c.Room)
.WithMany(s => s.Reservation)
.Map(t => t.MapLeftKey("RoomId")
.MapRightKey("ReservationId")
.ToTable("RoomReservation"));
}
After button pressing I save data into database:
public ActionResult Reservation(int number, string date, int roomid)
{
var reserv = new Reservation
{
NumberOfNights = number,
DateOfEntry = date
};
db.Reservation.Add(reserv);
db.SaveChanges();
}
How can I save data into table RoomReservation?
You need to retrieve the instances of Room from the database:
List<Room> selectedRooms = db.Rooms.Where(r => r.RoomId == roomid).ToList();
var reserv = new Reservation
{
NumberOfNights = number,
DateOfEntry = date,
Room = selectedRooms
};
db.Reservation.Add(reserv);
db.SaveChanges();

How to eager load child entities using repository pattern

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

Asp.net MVC 2 Entity Framework Generic Repository method. How to update a specific column?

I have 10 tables with the same design. Each of them have an IsActive column.
For example:
Category
CatID
CatName
IsActive
Product
PrdID
PrdName
IsActive
Is there a way to create a generic method to update the IsActive column.
public void Deactivate<T>(T TEntity)
{
// Put the code to update
// IsActive
}
I read about generic repository, but nothing explains how to update a specific column.
Thanks everyone.
The trick is to put a where type restriction for your generic type on your BaseRepository class. Try something similar to this:
WARNING: air code ;-)
Base model:
public interface IDbTable
{
bool IsActive { get; set; }
}
public class DbTable
{
public bool IsActive { get; set; }
}
Your model
public class Category : DbTable
{
public int CatId { get; set; }
public string CatName { get; set; }
}
public class Product : DbTable
{
public int PrdId { get; set; }
public string PrdName { get; set; }
}
Your repository
public interface IBaseRepository<T> where T : class, IDbTable
{
void Deactivate<T>(T entity);
}
public class BaseRepository<T> : IBaseRepository
{
public void Deactivate<T>(T entity)
{
entity.IsActive = false;
}
}
You could go even further and extend your IDbTable to include even more generic and helpful columns. E.g.
public interface IDbTable
{
int Id { get; set; }
bool IsActive { get; set; }
DateTime UpdatedOn { get; set; }
DateTime CreatedOn { get; set; }
}
Repo
public interface IBaseRepository<T> where T : class, IDbTable
{
T GetById(int id);
void Add(T entity);
void Update(T entity);
void Deactivate(T entity);
}
public class BaseRepository<T> : IBaseReposiotry<T>
{
public T GetById(int id)
{
//code to get the entity by id
}
public void Add(T entity)
{
entity.CreatedOn = DateTime.UtcNow;
entity.UpdatedOn = DateTime.UtcNow;
}
public void Update(T entity)
{
entity.UpdatedOn = DateTime.UtcNow;
}
public void Deactivate(T entity)
{
entity.IsActive = false;
}
}
These two articles should help you out as well:
new Repository().DoMagic()
Implementing a Simple Generic Repository with LinqToSql
HTHs,
Charles

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