EF7 Getting null values for entity's collection of entities which are many to many - asp.net-mvc

I am getting null values for the collection of entities nested in my top entity. How do I properly write my LINQ query so that these values aren't null??
I am using Entity Framework 7 and MVC 6 Here are my classes:
My models:
public class WorkStation
{
public Id { get; set; }
public string Name{ get; set; }
public ICollection<PersonWorkStation> PersonWorkStations{ get; set; }
}
public class Person
{
public Id { get; set; }
public string FirstName { get; set; }
public ICollection<PersonWorkStation> PersonWorkStations{ get; set; }
}
public class PersonWorkStation
{
public int Id { get; set; }
public int PersonId { get; set; }
public Person Person { get; set; }
public int WorkStationId { get; set; }
public WorkStation WorkStation { get; set; }
}
My DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PersonWorkStation>()
.HasKey(op => new { op.Id });
modelBuilder.Entity<PersonWorkStation>()
.HasOne(pt => pt.Person)
.WithMany(p => p.PersonWorkStation)
.HasForeignKey(pt => pt.PersonId);
modelBuilder.Entity<PersonWorkStation>()
.HasOne(pt => pt.WorkStation)
.WithMany(t => t.PersonWorkStation)
.HasForeignKey(pt => pt.WorkStationId);
base.OnModelCreating(modelBuilder);
}
So with that being said, when I bring back a person, and look at the "PersonWorkStation"s collection, the WorkStation property is null. How can I bring back that entity?
Here is how I am retrieving the data:
var person = _context.Persons
.Include(p => p.PersonWorkStation)
.FirstOrDefault(p => p.Id == 1);
return person;
Again, the person.PersonWorkStations.Workstation entity is null for all items in the person.PersonWorkStations collection. How do I return this entity?
Thanks!

I have found the answer, I needed to add this line:
var person = _context.Persons
.Include(p => p.PersonWorkStation)
.ThenInclude(p => p.WorkStation)
.FirstOrDefault(p => p.Id == 1);
return person;

Related

EF6 migration - FK to entity with composite Key error

I have the following entities:
public class MeetingAttendee
{
public int MeetingId { get; set; }
public Meeting Meeting { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public IEnumerable<MeetingAttendanceEntry> MeetingAttendanceEntries { get; set; }
}
public class MeetingAttendanceEntry
{
public int Id { get; set; }
public int MeetingId { get; set; }
public Meeting Meeting { get; set; }
public int MeetingAttendeeId { get; set; }
public MeetingAttendee MeetingAttendee { get; set; }
public AttendanceStatus AttendanceStatus { get; set; }
}
The MeetingAttendee is set as:
modelBuilder.Entity<MeetingAttendee>().HasKey(pk => new { pk.MeetingId, pk.UserId });
modelBuilder.Entity<MeetingAttendee>().HasRequired(o => o.User).WithMany(m => m.MeetingAttendees).HasForeignKey(fk => fk.UserId);
modelBuilder.Entity<MeetingAttendee>().HasRequired(o => o.Meeting).WithMany(m => m.Attendees).HasForeignKey(fk => fk.MeetingId);
And when I'm trying to add MeetingAttendanceEntry, I'm getting "The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical" validation error.
I think is coming from the composite key, but everything I tried fails.
I tried from the parent entity:
modelBuilder.Entity<MeetingAttendee>().HasMany(m => m.MeetingAttendanceEntries).WithRequired(e => e.MeetingAttendee).HasForeignKey(e => e.MeetingAttendeeId);
From the child:
modelBuilder.Entity<MeetingAttendanceEntry>().HasRequired(e => e.MeetingAttendee).WithMany(m => m.MeetingAttendanceEntries).HasForeignKey(e => e.MeetingAttendeeId);
I tried adding the User and UserId to MeetingAttendanceEntry, and then setting it as FK as well:
modelBuilder.Entity<MeetingAttendanceEntry>().HasRequired(e => e.User).WithMany().HasForeignKey(e => e.UserId);
but still no luck.
What I'm missing?

How can I show the value of navigation property in asp.net mvc

CategoryName
Index method:
public ActionResult Index(int? page)
{
var data = objContext.Products.ToList().ToPagedList(page ?? 1, 1);
return View(data);
}
View
if in your product poco, you have a navigation property for Category of Product, simply use:
#Html.DisplayNameFor(modelItem => model.Category.Title)
assuming your product and category are as follow:
public class Product {
public int ID { get; set; }
public string ProductName { get; set; }
public int CategoryId { get; set; }
// and other properties
public virtual ProductCategory ProductCategory { get; set; }
}
public class ProductCategory {
public int ID { get; set; }
public string CategoryName { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
and in your map definition for product you have :
internal class ProductMap : EntityTypeConfiguration<Product> {
public ProductMap () {
// Primary Key
this.HasKey(t => t.ID);
// Properties, for example
this.Property(t => t.ProductName )
.HasMaxLength(200);
// Table Mappings
this.ToTable("Product");
//Relationships
this.HasRequired(t => t.ProductCategory)
.WithMany(t => t.Products)
.HasForeignKey(d => d.CategoryId );
}
}

EF Code First - Populating data in Many to Many

I'm new to ASP.Net MVC and want to create a simple Blog project, therefore I have two entity posts and categories. each post can belong to many categories and each category can belong to many posts.
Models.cs
public class Category
{
[Key]
public int CategoryId { get; set; }
public int? ParentId { get; set; }
public virtual Category Parent { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public virtual ICollection<News> News { get; set; }
public Category()
{
News = new List<News>();
}
}
public class News
{
[Key]
public int NewsId { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string Content { get; set; }
public string Source { get; set; }
public string SourceURL { get; set; }
public string Images { get; set; }
public string Password { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? ModifiedAt { get; set; }
public DateTime? DeletedAt { get; set; }
public string CreatedBy { get; set; }
public string DeletedBy { get; set; }
public virtual PublishPeriod PublishPeriodId { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public News()
{
Categories = new List<Category>();
}
}
ModelsMap.cs
public class CategoryMap:EntityTypeConfiguration<Category>
{
public CategoryMap()
{
Property(one => one.Title).HasMaxLength(100).IsRequired();
HasOptional(x => x.Parent).WithMany().HasForeignKey(x => x.ParentId);
}
}
public class NewsMap:EntityTypeConfiguration<News>
{
public NewsMap()
{
Property(x => x.CreatedBy).HasMaxLength(150);
Property(x => x.DeletedBy).HasMaxLength(150);
Property(x => x.Title).IsRequired().HasMaxLength(150);
Property(x => x.Summary).IsRequired();
Property(x => x.Content).IsRequired().HasColumnType("ntext");
Property(x => x.CreatedAt).HasColumnType("datetime");
Property(x => x.Password).IsOptional().HasMaxLength(128);
Property(x => x.DeletedAt).IsOptional();
Property(x => x.ModifiedAt).IsOptional();
HasMany(x => x.Categories).WithMany(x => x.News).Map(x =>
{
x.ToTable("NewsCategories");
x.MapLeftKey("News_NewsId");
x.MapRightKey("Category_CategoryId");
});
}
}
And DB Context
public DbSet<Category> Categories { get; set; }
public DbSet<News> News { get; set; }
public DbSet<PublishPeriod> PublishPeriod { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new CategoryMap());
modelBuilder.Configurations.Add(new NewsMap());
modelBuilder.Configurations.Add(new PublishPeriodMap());
I have a create view for posts that displays categories in a list with checkboxs and each checkbox value is category's ID. How can I insert or update posts and keep relation between post and categories.
NewsController
//
// POST: /Admin/News/Create
[HttpPost]
public ActionResult Create(News news, List<string> Category)
{
ViewBag.Categories = catRepository.All.OrderBy(x => x.Title);
if (ModelState.IsValid)
{
foreach (var item in Category)
{
news.AddCategory(catRepository.Find(int.Parse(item)));
}
news.CreatedAt = DateTime.Now;
news.CreatedBy = "M.Hesabi";
newsRepository.InsertOrUpdate(news);
newsRepository.Save();
return RedirectToAction("Index");
}
else
{
return View();
}
}
UPDATE: I created a method in News Model as #DanS said and edited my controller.
I'd recommend creating a method on the News class:
public void AddCategory(Category category) {
Categories.Add(category);
category.News.Add(this);
}
From your Controller you can then add each selected Category to the News instance, and then add the News to the DbContext prior to calling SaveChanges. This may depend, however, on how your repositories make use of the context -- in that if they open their own, instead of accessing a shared context, you might have to attach the categories to the News repository's context prior to saving. Hopefully this helps...
Update
IEntityChangeTracker error:
It appears as if MVCScaffolding uses a separate context for each repository. As mentioned, having separate contexts can lead to some additional required steps. As it stands now, your categories are tracked by Context A while your news is tracked by Context B-- You could detach/attach the category entities between the two contexts, but I'd say the recommended solution would be to change your repositories to accept a shared context through their constructors.
I'm assuming that you are instantiating the repositories in the controller's constructor, rather than using dependency injection, so you would modify your constructor code to do something like the following:
myContext = new YourContextClass();
catRepository = new CategoryRepository(myContext);
newsRepository = new NewsRepository(myContext);
You would then have to add the constructors to your repositories to assign the internal context property, and finally, adjust your controller to properly dispose of the context.
protected override void Dispose(bool disposing)
{
if (disposing)
myContext.Dispose();
base.Dispose(disposing);
}

DataContext is missing properties in where statement

Could some tell me why I have no properties in my where statements after using a select statement for e.g.
db.Select(x => x.Lft).Where(x => x.DepartmentId == id);
// missing properties in the where clause
And could you help me correct my code to implement it please leave me an example of what to do to implement this thanks
Classes:
public class Department
{
public Department()
{
Products = new List<Product>();
}
public long DepartmentId { get; set; }
[Required(ErrorMessage="Please enter a name for the departments.")]
[DataType(DataType.Text)]
public string Name { get; set; }
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please enter a valid url for the department.")]
public string Url { get; set; }
public int Lft { get; set; }
public int Rgt { get; set; }
public bool MenuItem { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
my DataContext class
internal class DepartmentsTypeConfiguration : EntityTypeConfiguration<Department>
{
public DepartmentsTypeConfiguration()
{
Property(department => department.DepartmentId)
.HasColumnName("DepartmentId")
.HasDatabaseGeneratedOption(databaseGeneratedOption: DatabaseGeneratedOption.Identity);
Property(department => department.Name)
.HasColumnName("Name")
.IsRequired();
HasKey(key => key.DepartmentId)
.HasMany(x => x.Products)
.WithRequired(x => x.Department)
.WillCascadeOnDelete(true);
}
}
public class LeapFrogDataContext : DbContext
{
public DbSet<Department> Departments { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<ProductSpecification> ProductSpecifications {get; set;}
public DbSet<Specification> Specifications { get; set; }
/**/
static LeapFrogDataContext()
//: base("name=LeapFrogDataConnection")
{
//Database.SetInitializer(new LeapFrogInitializer());
//Database.SetInitializer(new DropCreateDatabaseIfModelChanges<LeapFrogDataContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new DepartmentsTypeConfiguration());
modelBuilder.Configurations.Add(new ProductsTypeConfiguration());
modelBuilder.Configurations.Add(new SpecificationsTypeConfiguration());
modelBuilder.Configurations.Add(new ProductSpecificationsTypeConfiguration());
base.OnModelCreating(modelBuilder);
}
}
db.Select(x => x.Lft) returns a list of int so in the where clause you will not access any property.
I guess you may switch select and where to achieve what you want. Assume db is the actual context.
db.Where(x => x.DepartmentId == id).Select(x => x.Lft)
That's a bit weird. Normally it should look like
db.context.Departments.Where(x => x.DepartmentId == id).Select(x => x.Lft)

Entity framework to object with collection, with Automapper, exception on update

I have an EF 4.1 model, two tables are generated PERSON and ADDRESS from my database.
//This method works
public void Update(IPerson person)
{
var personDb = _dataContext.PERSON.SingleOrDefault(x => x.ID == person.Id);
Mapper.Map<Person, PERSON>((Person)person, personDb);
_dataContext.SaveChanges();
}
But when I remove the .Ignore() in Automapper mapping, I get this exception :
The EntityCollection could not be initialized because the relationship manager for the object to which the EntityCollection belongs is already attached to an ObjectContext. The InitializeRelatedCollection method should only be called to initialize a new EntityCollection during deserialization of an object graph.
I'd like when I added an new address to the existing addresses save the person and address.
Any idea ?
Thanks,
public void AutomapperInit()
{
Mapper.CreateMap<Person, PERSON>()
.ForMember(x => x.ADDRESS, opt => opt.Ignore());
Mapper.CreateMap<PERSON, Person>()
.ForMember(dest => dest.Address, option => option.MapFrom(src => src.ADDRESS.Select(address => Mapper.Map<ADDRESS, Address>(address)).ToList()));
Mapper.CreateMap<Address, ADDRESS>();
Mapper.CreateMap<ADDRESS, Address>()
.ForMember(dest => dest.Rue, option => option.MapFrom(src => src.STREET));
}
public interface IPerson
{
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
ICollection<IAddress> Address { get; set; }
}
public interface IAddress
{
string Rue { get; set; }
string Number { get; set; }
int PersonId { get; set; }
}
class Person : IPerson
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<IAddress> Address { get; set; }
}
class Address : IAddress
{
public string Rue { get; set; }
public string Number { get; set; }
public int PersonId { get; set; }
}
Does
var personDb = Mapper.Map<Person, PERSON>((Person)person, personDb);
_dataContext.Attach(personDb);
_dataContext.SaveChanges();
give the results you expect, or am I misinterpreting your question?

Resources