Entity framework one to many - empty virtual collection - entity-framework-6

I have this code first database
public partial class SystemWarning
{
[Key]
public long Id { get; set; }
/// <summary>
/// id of the admin that created the entry
/// </summary>
public string CreatedById { get; set; }
public virtual AspNetUser CreatedBy { get; set; }
public string AcknowledgedById { get; set; }
public virtual AspNetUser AcknowledgedBy { get; set; }
}
public partial class AspNetUser
{
public AspNetUser()
{
SystemWarnings = new HashSet<SystemWarning>();
}
[Key]
public string Id { get; set; }
public virtual ICollection<SystemWarning> SystemWarnings { get; set; }
}
And linked together as follows
modelBuilder.Entity<AspNetUser>()
.HasMany(e => e.SystemWarnings)
.WithOptional(e => e.CreatedBy)
.HasForeignKey(e => e.CreatedById).WillCascadeOnDelete(false);
For reasons that escape me at the moment, when I extract my AspNetUser, the SystemWarnings collection is always empty, even if there are systemwarnings that are linked to the AspNetUser in the database.
I have a bunch of these 1-n links, even on the same object, and the other links remain non empty, and for now I'm not seeing the difference.
#edit: here's that other object for comparison:
public partial class UserProfile: BaseObject
{
public Guid Id { get; set; }
public string OwnerId { get; set; }
public virtual AspNetUser Owner { get; set; }
}
and the mapping
modelBuilder.Entity<AspNetUser>()
.HasMany(e => e.OwnedUserProfiles)
.WithOptional(x => x.Owner)
.HasForeignKey(x => x.OwnerId);
Seems the same to me, except that the SystemWarning has a long as Id with identity specs (forgot to post that before - it's defined as follows)
modelBuilder.Entity<SystemWarning>().Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
And that SystemWarning actually has another reference to the AspNetUser that I now added (AcknowledgedBy(Id)) which is linked as follows
modelBuilder.Entity<AspNetUser>()
.HasMany(e => e.SystemWarnings)
.WithOptional(e => e.AcknowledgedBy)
.HasForeignKey(e => e.AcknowledgedById).WillCascadeOnDelete(false);
I'll post the SQL trace soon...

Well, that edit did the trick.. both links from SystemWarning to AspNetUser are mapped on the SystemWarnings collection.. no wonder that's not working the way it's supposed to.
Note to myself.. map every 1:n to a different collection or there'll be trouble.

Related

Implementing Many-To-Many relationship in Asp.Net (confused?)

My current aim is to build a database structure using classes in Entity Framework & ASP MVC.
I currently have a Users table and a Posts table. What I would like to do is create a many to many relationship for Users who have liked Posts (whilst conserving who created the post). And be able to access for each user all of the posts they have liked. Currently I have these classes but I'm unsure of how to link them as all of the online examples are linking Primary Keys from different databases where I just want to use the Username Parameter. Any help would be great. I have this so far.
public class Posts
{
[Key]
public virtual int PostId { get; set; }
public virtual string Title { get; set; }
public virtual string URL { get; set; }
[DisplayName("Main Text")]
public virtual string TextBody { get; set; }
public int PostLikes { get; set; }
private DateTime Datedata = DateTime.Now;
public DateTime PostDate { get { return Datedata; } set { Datedata = value; } }
public virtual Users User { get; set; }
public ICollection<PostLikes> UsersWhoHaveSigned { get; set; }
}
{
public class Users
{
[Key]
public virtual int UserId { get; set; }
public virtual string Username { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual List<Posts> Post { get; set; }
}
}
I have not built the UsersWhoHaveSigned table yet. Early experimentation caused me so much backtracing it was painful. Any help would be great.
Edit: I was hoping to ask for help and then appropriate that informtaion to fit my example which utilises the individual accounts add-on. This produces some addition files that are now causing interference with the code you've provided.
Here is the IdentityModels.cs file.
using System.Data.Entity;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
namespace Coursework2.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit https://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
}
I believe that the assembly directives at the top are preventing system.data.entity from being used so when I try to implement ApplicationDbContext : DbContext I get error messages :/
Ideally I'm looking to use the IdentityModels.cs file as a replacement for the users class. But still very lost.
First of all, I recommend that you use the singular form for your class names, as EF will automatically pluralize table names.
Second, for a key property, you can just use the term Id, without any annotations, and EF will pick it up as the principal key.
Finally, I'll assume you are looking to use a Code-First approach. Consider the following classes (yours, but refactored for clarity purpose):
public class Post
{
public virtual Guid Id { get; set; }
public virtual string UserName { get; set; }
public virtual User User { get; set; }
public virtual ICollection<PostLike> Likes { get; set; }
}
public class PostLike
{
public virtual Guid Id { get; set; }
public virtual Guid PostId { get; set; }
public virtual Post Post { get; set; }
public virtual string UserName { get; set; }
public virtual User User { get; set; }
}
public class User
{
public virtual Guid Id { get; set; }
public virtual string UserName { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual ICollection<PostLike> Likes { get; set; }
}
To make it work, you'd need a DbContext such as the following. Pay attention to the OnModelCreating method, which is where the magic happens:
public class ApplicationDbContext
: DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<PostLike> PostLikes { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasAlternateKey(u => u.UserName);
modelBuilder.Entity<User>()
.HasMany(u => u.Posts)
.WithOne(p => p.User);
modelBuilder.Entity<Post>()
.HasOne(p => p.User)
.WithMany(u => u.Posts)
.HasForeignKey(p => p.UserName)
.HasPrincipalKey(u => u.UserName);
modelBuilder.Entity<Post>()
.HasMany(p => p.Likes)
.WithOne(pl => pl.Post);
modelBuilder.Entity<PostLike>()
.HasOne(pl => pl.Post)
.WithMany(p => p.Likes);
modelBuilder.Entity<PostLike>()
.HasOne(pl => pl.User)
.WithMany(u => u.Likes)
.HasForeignKey(pl => pl.UserName)
.HasPrincipalKey(u => u.UserName);
}
}
Voila! I hope it answers your question ;)
If so, please don't forget to mark my post as the answer!
Edit:
I'll provide some explanations, that I had left out to answer your question ASAP.
So, first thing you need to do, is to declare the UserName as an alternate key, because you want to create relationships depending on it, and you already have the 'Id' principal key declared.
Second, on each object that should own a User reference base on the UserName alternate key, you need to declare the object's UserName property as the foreign key of the relationship, and the User's UserName property as the principal key.
In other words, the foreign key is the property that a referencing object uses for the relationship, and the principal key is the property based on which the referenced object is bound to the referencing one.
Note that principal keys must have a key or alternate key constraint, or it won't work.
Just to comment on your answer. I found that I had to use
using Microsoft.EntityFrameworkCore and remove System.Data.Entity - This was causing the program to be confused as to which DbContext I wanted to use. Thanks!

Defining multiple Foreign Key for the Same table in Entity Framework Code First

I have two entities in my MVC application and I populated the database with Entity Framework 6 Code First approach. There are two city id in the Student entity; one of them for BirthCity, the other for WorkingCity. When I define the foreign keys as above an extra column is created named City_ID in the Student table after migration. Id there a mistake or how to define these FKs? Thanks in advance.
Student:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
public virtual City LivingCity { get; set; }
}
City:
public class City
{
public int ID { get; set; }
public string CityName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
To achieve what you want you need to provide some aditional configuration.Code First convention can identify bidirectional relationships, but not when there are
multiple bidirectional relationships between two entities.You can add configuration (using Data Annotations or the Fluent API) to present this
information to the model builder. With Data Annotations, you’ll use an annotation
called InverseProperty. With the Fluent API, you’ll use a combination of the Has/With methods to specify the correct ends of these relationships.
Using Data Annotations could be like this:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
[InverseProperty("Students")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
public virtual City LivingCity { get; set; }
}
This way you specifying explicitly that you want to relate the BirthCity navigation property with Students navigation property in the other end of the relationship.
Using Fluent Api could be like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity)
.WithMany(m => m.Students).HasForeignKey(m=>m.BirthCityId);
modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity)
.WithMany().HasForeignKey(m=>m.LivingCityId);
}
With this last solution you don't need to use any attibute.
Now, the suggestion of #ChristPratt in have a collection of Student in your City class for each relationship is really useful. If you do that, then the configurations using Data Annotations could be this way:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
[InverseProperty("BirthCityStudents")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
[InverseProperty("LivingCityStudents")]
public virtual City LivingCity { get; set; }
}
Or using Fluent Api following the same idea:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity)
.WithMany(m => m.BirthCityStudents).HasForeignKey(m=>m.BirthCityId);
modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity)
.WithMany(m => m.LivingCityStudents).HasForeignKey(m=>m.LivingCityId);
}
Sheesh. It's been a long day. There's actually a very big, glaring problem with your code, actually, that I completely missed when I commented.
The problem is that you're using a single collection of students on City. What's actually happening here is that EF can't decide which foreign key it should actually map that collection to, so it creates another foreign key specifically to track that relationship. Then, in effect you have no navigation properties for the collections of students derived from BirthCity and LivingCity.
For this, you have to drop down to fluent configuration, as there's no way to configure this properly using just data annotations. You'll also need an additional collection of students so you can track both relationships:
public class City
{
...
public virtual ICollection<Student> BirthCityStudents { get; set; }
public virtual ICollection<Student> LivingCityStudents { get; set; }
}
Then, for Student:
public class Student
{
...
public class StudentMapping : EntityTypeConfiguration<Student>
{
public StudentMapping()
{
HasRequired(m => m.BirthCity).WithMany(m => m.BirthCityStudents);
HasRequired(m => m.LivingCity).WithMany(m => m.LivingCityStudents);
}
}
}
And finally in your context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new Student.StudentMapping());
}

Enitiy Framework Query, Filtering child collections of child collections

I'm using eager loading to populate my object. Is it possible to filter child collections of child collections. The following code gives me the child collection of filtered periods. But I would also like to filter the child of periods where TradeStatus == TradeStatus.Open?
public class Route
{
public Guid RouteId { get; set; }
public string Name { get; set; }
public virtual ICollection<Period> Periods { get; set; }
}
public class Period
{
public Guid PeriodId { get; set; }
public virtual ICollection<Trade> Trades { get; set; }
public bool IsActive { get; set; }
}
public class Trade
{
public Guid PeriodId { get; set; }
public string TradeName { get; set; }
public decimal TradePrice { get; set; }
public TradeStatus TradeStatus { get; set; }
}
string routeName = "UK_USA"
Route route = Context.Set<Route>().SingleOrDefault(r => r.Name.Equals(routeName));
if (route != null)
{
Context.Entry(route).Collection(r => r.Tenors).Query()
.Where(t => t.IsActive)
.Include(t => t.Trades).Load();
}
I've tried the following but it returns
The Include path expression must refer to a navigation property
defined on the type. Use dotted paths for reference navigation
properties and the Select operator for collection navigation
properties. Parameter name: path
Context.Entry(route).Collection(r => r.Tenors).Query()
.Where(t => ValidTenorName.Contains(t.Name))
.Include(t =>
t.Trades.Where(tr=> tr.TradeStatus == TradeStatus.Open).Load();
Is there a solution to this problem, or an alternative way using joins perhaps?
Thanks

Entity Framework Data Annotations equivalent of .WillCascadeOnDelete(false);

I'm currently using EF Code First 4.3 with migrations enabled, but automatic migrations disabled.
My question is simple, is there a data annotations equivalent of the model configuration .WillCascadeOnDelete(false)
I would like to decorate my class so that the foreign key relationships do NOT trigger a cascading delete.
Code sample:
public class Container
{
public int ContainerID { get; set; }
public string Name { get; set; }
public virtual ICollection<Output> Outputs { get; set; }
}
public class Output
{
public int ContainerID { get; set; }
public virtual Container Container { get; set; }
public int OutputTypeID { get; set; }
public virtual OutputType OutputType { get; set; }
public int Quantity { get; set; }
}
public class OutputType
{
public int OutputTypeID { get; set; }
public string Name { get; set; }
}
I Would like to do something like this:
public class Output
{
[CascadeOnDelete(false)]
public int ContainerID { get; set; }
public virtual Container Container { get; set; }
[CascadeOnDelete(false)]
public int OutputTypeID { get; set; }
public virtual OutputType OutputType { get; set; }
public int Quantity { get; set; }
}
This way i would be able to scaffold the migration correctly. which scaffolds the foreign key relationships to be cascade deleted at the moment.
Any ideas, other than using Model Configuration?
No there is no such equivalent. You must use fluent API to remove cascade delete selectively or you must remove OneToManyCascadeDelete convention to remove it globally.
Create a mapping class (the fluent syntax) and use the code below:
// add relationships "Post" and "User" to a "Comment" entity
this.HasRequired(t => t.Post)
.WithMany(t => t.Comments)
.HasForeignKey(d => d.PostID)
.WillCascadeOnDelete(false); // <---
this.HasOptional(t => t.User)
.WithMany(t => t.Comments)
.HasForeignKey(d => d.UserID)
.WillCascadeOnDelete(false); // <---
Here's a nice post on how to set up fluent mappings if you need more info.
Just make the FK property nullable can prevent cascade delete from happening:
public int? OutputTypeID { get; set; }

Navigation property of entity is not loading

I am building a reservation system. I have users in roles('admin', 'client', 'employee', 'student').
Each reservation must be associated with a user of role client, it might be assigned to user of role employee and might also be assigned to user of role student.
So in my reservation class I have properties of type User and I have marked them with [ForeignKey("AnytypeId")] attribute to hint EF for relations.
I have seen code like this at http://blog.stevensanderson.com/2011/01/28/mvcscaffolding-one-to-many-relationships/
public class Reservation
{
public int ReservationID
{
get;
set;
}
[Required(ErrorMessage="Please provide a valid date")]
public DateTime ReservationDate
{
get;
set;
}
public DateTime ReservationEnd { get; set; }
public DateTime EntryDate
{
get;
set;
}
public DateTime UpdatedOn
{
get;
set;
}
public decimal Ammount
{
get;
set;
}
public decimal? Discount { get; set; }
[DataType(DataType.MultilineText)]
public string ServiceDetails { get; set; }
[DataType(DataType.MultilineText)]
public string Remarks { get; set; }
public String PaymentMethod { get; set; }
public string VoucherNumber { get; set; }
public int ServiceID
{
get;
set;
}
public virtual Service Service
{
get;
set;
}
public string EmployeeID { get; set; }
[ForeignKey("EmployeeID")]
public virtual User Employee { get; set; }
public string ClientID { get; set; }
[ForeignKey("ClientID")]
public virtual User Client { get; set; }
public string StudentID { get; set; }
[ForeignKey("StudentID")]
public virtual User Student { get; set; }
}
public class ReservationMap : EntityTypeConfiguration<Reservation>
{
public ReservationMap()
{
this.HasOptional(r => r.Client).WithMany().WillCascadeOnDelete(true);
this.HasOptional(r => r.Employee).WithMany().WillCascadeOnDelete(false);
this.HasOptional(r=>r.Student).WithMany().WillCascadeOnDelete(false);
}
}
Now as I run my mvc3 EF code first app database created for me on the fly with following ERD and edmx model.
Now few problems that I am having:
1. When I am listing all the users of role clients in view their reservation property is showing always 0 even if their are reservations available in database. I don't know why this collection property marked with virtual is not loading??
Please I am stuck with this help me out here this is the last thing remaining.
There are couple of problems in your model. You have configured the one to many relationship in such a way that the many end(Reservations property) is excluded from the mapping. Hence the Reservations will not be loaded by EF.
The other problem is if you are going to map the Reservations property as the many end of the relationship, what will be the navigational property? is it Employee, Client, Student? Because only one of these properties can participate in the relationship with Reservations property.
It is not clear as to how these relationships should be modeled by your description. One way would be to have 3 collection properties.
public class ReservationMap : EntityTypeConfiguration<Reservation>
{
public ReservationMap()
{
HasOptional(r => r.Client).WithMany(u => u.ClientReservations).WillCascadeOnDelete(true);
HasOptional(r => r.Employee).WithMany(u => u.EmployeeReservations).WillCascadeOnDelete(false);
HasOptional(r=>r.Student).WithMany(u => u.StudentReservations).WillCascadeOnDelete(false);
}
}

Resources