One To Many Relationships field always null - entity-framework

Hi Everyone When I was trying to use Model.SubCategories but it is always null. I Don't understand anything.
MyModel like this:
public class Category
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int CategoryID { get; set; }
public string Name { get; set; }
public virtual IEnumerable<SubCategory> SubCategories { get; set; }
}
public class SubCategory
{
[Key]
public int SubCategoryID { get; set; }
public string Name { get; set; }
[ForeignKey("Category")]
public int CategoryID { get; set; }
public virtual Category Category { get; set; }
}
DBContext Like This:
public class EntitiesContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<SubCategory> SubCategories { get; set; }
}
My Action Like this:
public ActionResult Index()
{
return View(database.Categories.ToList());
}
My Views snippet Like This:
#foreach (var item in Model.SubCategories) //always null
{
//...
}

Try to declare SubCategory property as ICollection instead of IEnumerable.

SubCategories are virtual, which means lazy loading. That means, the table will not be retrieved, unless you request it. Use Include method.
public ActionResult Index()
{
return View(database.Categories.Include("SubCategory").ToList());
}

Related

Insert in relationship table after inserting record

I have this classes and a WebApi method to POST the Item.
After inserting Item i want it to insert the inserted PK ItemId and given CategoryId into CategoryItem
public partial class Item
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Description { get; set; }
public string? Type { get; set; }
}
public partial class Category
{
public int Id { get; set; }
public string? Description { get; set; }
public string? Name { get; set; }
}
public partial class Categoryitem
{
public int IdCategory { get; set; }
public int IdItem { get; set; }
public virtual Category IdCategoryNavigation { get; set; } = null!;
public virtual Item IdItemNavigation { get; set; } = null!;
}
[HttpPost]
public ActionResult<Item> PostItem(Item item)
{
_context.Item.Add(item);
_context.SaveChanges();
return Ok();
}
How do i pass the IdCategory into POST method ? Do I need a CategoryCollection in Item class and pass IdCategory through Item member ?
What about the relationship table ? How do i insert the two Ids ?
Thank you
you can create a viewmodel if you need to add several categories in the same time when you create an item, or if you only assign one category when you create an item you can just add CategoryId, but with attribute [NotMapped]
public partial class Item
{
public int Id { get; set; }
... another properties
[NotMapped]
public int? CategoryId{ get; set; }
}
or fluent api
modelBuilder.Entity<Item>().Ignore(c => c.CategoryId);
you will have to bind this CategoryId to dropdown list in the view
and action
[HttpPost]
public ActionResult<Item> PostItem(Item item)
{
var categoryitem = new Categoryitem
{
IdItemNavigation=item,
IdCategory = item.CategoryId
};
_context.Categoryitem.Add(categoryitem);
_context.SaveChanges();
return Ok(item);
}

DbSet property of type class returns null

I'm creating an API for an app. The DbContext I have trouble with looks like this:
public class SchoolPlannerDbContext : DbContext
{
public SchoolPlannerDbContext(DbContextOptions<SchoolPlannerDbContext> options) : base(options) { }
public DbSet<Activity> Activities { get; set; }
public DbSet<Room> Rooms { get; set; }
public DbSet<Subject> Subjects { get; set; }
public DbSet<Teacher> Teachers { get; set; }
public DbSet<Group> Groups { get; set; }
}
The Activity class is as follows:
public class Activity
{
public int ID { get; set; }
[Required]
public Teacher Teacher { get; set; }
[Required]
public Room Room { get; set; }
[Required]
public Subject Subject { get; set; }
[Required]
public Group Group { get; set; }
[Required]
public int Slot { get; set; }
[Required]
public int Day { get; set; }
}
All the other properties contain an int ID and a string Name.
My controller looks like this:
public class SqlPlannerData : ISchoolPlannerData
{
private readonly SchoolPlannerDbContext db;
public SqlPlannerData(SchoolPlannerDbContext db)
{
this.db = db;
}
public IEnumerable<Activity> GetActivities()
{
return db.Activities;
}
public IEnumerable<Group> GetGroups()
{
return db.Groups;
}
}
GetGroups() works as intended and returns an IEnumerable with properties set correctly.
My problem is that when I'm trying to access db.Activities, the properties of type, say, Teacher (non-basic types like int) are set to null:
Debugger screenshot.
However, there is a row in the database that looks like this. I.e. the columns exist in the database.
What do I do to make GetActivities() return an IEnumerable with correctly set properties?
Some properties are null because of lazy loading you need to include them
return db.Activities
.Include(i => i.Teacher)
.Include(i => i.Room)
.Include(i => i.Subject)
.Include(i => i.Group)
.ToList()
Each propety Id can be configured by EF5+ as shadows. But I usually prefer to add all Ids explicitely. This way I have much less problem when I am using db context in the project. But is is optional and you can leave it as is
public class Activity
{
public int ID { get; set; }
[Required]
public int? TeacherId { get; set; }
[Required]
public int? RoomId { get; set; }
[Required]
public int? SubjectId { get; set; }
[Required]
public int? GroupId { get; set; }
public virtual Teacher Teacher { get; set; }
public virtual Room Room { get; set; }
public virtual Subject Subject { get; set; }
public virtual Group Group { get; set; }
[Required]
public int Slot { get; set; }
[Required]
public int Day { get; set; }
}
and in order to get list activities you have to add ToList() or ToArray() at least
public IEnumerable<Activity> GetActivities()
{
return db.Activities.ToArray();
}
and by the way, you can' t using not nullabe Id as required becaue it is relevant
[Required]
public int TeacherId { get; set; }
since int by default is 0 and it is a valid value and required will not be working

How to build an Parent Child in EF 7

I have a table that looks like the following:
public class ServiceType
{
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
The Service type data has another collection of "SubTypes", which are basically a collection of ServiceType.Code. I would like to build a navigation property that references the ServiceType collection.
So I am guessing that I need to build another table to store the Id of the Parent and of the child like:
public class ServiceTypeHeirarchy
{
public int Id { get; set; }
public int ParentId { get; set; } // ServiceType.Id of Parent
public int ChildId { get; set; } // ServiceType.Id of Child
}
Not sure if I am on the right track.
It seems that you want to have many 'sub'ServiceType under one 'parent'ServiceType.
And if that is indeed the case then you can do something like following:
public class ServiceType
{
public int Id { get; set; }
public int? ParentServiceTypeId { get; set; }
public ServiceType ParentServiceType { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public ICollection<ServiceType> ChildServiceTypes { get; set; }
}
And then you might want to do an Entity Configuration with HasMany, WithOptional.

Adding a sub collection of new object and my new object into the database with Entity framework

The method is really simple and I don't see what am I missing...
public int SaveEvent(Data.Models.Event evnt)
{
db.Events.Add(evnt);
db.SaveChanges();
return evnt.EventId;
}
here is the object declaration:
public class Event
{
public int EventId { get; set; }
public string Name { get; set; }
public ICollection<EventTag> EventTags { get; set; }
}
The evnt object contains a property name EventTags that contains 6 new elements.
The evnt is inserted in the database but not the EventTag... any idea ? no error nothing. just the EventTag are not added...
public class EventDbContext : DbContext
{
public DbSet<Event> Events { get; set; }
public DbSet<EventTag> EventTags { get; set; }
public DbSet<Tag> Tags { get; set; }
}
Here is a screenshot of the value:
If the EventTags are not being added to the database you may need to manually specify the EntityState for each tag.
public int SaveEvent(Data.Models.Event evnt)
{
foreach(var tag in evnt.EventTags)
{
db.Entry(tag).State = EntityState.Added;
}
db.Events.Add(evnt);
db.SaveChanges();
return evnt.EventId;
}
You might also want to update your class definition and set the EventTags property as virtual.
public class Event
{
public int EventId { get; set; }
public string Name { get; set; }
public virtual ICollection<EventTag> EventTags { get; set; }
}
In your screenshot it looks like the tags are loading, but not the Location property on the tags. If that's the case, then make sure to set the Location property to virtual as well.

EF 4.1 code first still looking for old table that no longer exists

I am using Entity Framework 4.1 code first.
I had a table in the database with the name MaritalStatus. I deleted it and created a new table in its place called MaritalStatuses. Whenever I try to get all the records from the table I get an error:
Invalid object name 'dbo.MaritalStatus'.
The query that it is trying to execute is:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[IsActive] AS [IsActive]
FROM [dbo].[MaritalStatus] AS [Extent1]}
Why would it still be looking for table MaritalStatus that I deleted? Can it be possible that it is cached somewhere? I dropped the whole database and recreated it via scripts. Still no luck. Maybe it has an issue when it comes to the "es" part of the name?
Context class:
public class HefContext : DbContext
{
public DbSet<Bank> Banks { get; set; }
public DbSet<AccountType> AccountTypes { get; set; }
public DbSet<MaritalStatus> MaritalStatuses { get; set; }
}
View model (with partial properties):
public class EditGrantApplicationViewModel
{
public int Id { get; set; }
public string EmployeeNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int MaritalStatusId { get; set; }
public IEnumerable<MaritalStatus> MaritalStatuses { get; set; }
public int BankId { get; set; }
public IEnumerable<Bank> Banks { get; set; }
public int AccountTypeId { get; set; }
public IEnumerable<AccountType> AccountTypes { get; set; }
}
Dropdown list in view for MaritalStatuses:
<td><label>Marital Status:</label> <span class="red">**</span></td>
<td>
#Html.DropDownListFor(x => x.MaritalStatusId, new SelectList(Model.MaritalStatuses, "Id", "Name", Model.MaritalStatusId), "-- Select --")
#Html.ValidationMessageFor(x => x.MaritalStatusId)
</td>
Controller:
public ActionResult Create()
{
EditGrantApplicationViewModel viewModel = new EditGrantApplicationViewModel
{
MaritalStatuses = maritalStatusService.GetAll(),
Banks = bankService.GetAll(),
AccountTypes = accountTypeService.GetAll()
};
return View(viewModel);
}
Service:
public IEnumerable<MaritalStatus> GetAll()
{
return maritalStatusRepository.GetAll();
}
Repository:
HefContext db = new HefContext();
public IEnumerable<MaritalStatus> GetAll()
{
return db.MaritalStatuses;
}
Model class:
public class MaritalStatus
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
You may have renamed the table in your database, but your Model class is still tied to the old name. You need to map the entity to the desired table name in the OnModelCreating method of your DbContext object
public class HefContext : DbContext
{
public DbSet<Bank> Banks { get; set; }
public DbSet<AccountType> AccountTypes { get; set; }
public DbSet<MaritalStatus> MaritalStatuses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MaritalStatus>().ToTable("MaritalStatuses");
}
}
Update:
Apparently EF code first has some issues with the pluralization of some table names, such as Status. Out of curiosity I tested a few others and found several which also had this same issue.
I guess this is wrong:
public DbSet<MaritalStatus> MaritalStatuses { get; set; }
Should be:
public DbSet<MaritalStatuses> MaritalStatuses { get; set; }