Creating 2 Relationships Between Two Entities Using Entity Framework Code First - ef-code-first

I'm trying to define a one to many relationship, as well as a one to one relationship between the same 2 entities "UserProfile" and "Blog". I think I have succeeded with the following code, however, it results in creating a new column in "Blog" table called "UserProfile_UserId" (FK). I don't understand why it does this.
The relationships in English are:
1. "A UserProfile has many Blogs"
2. "A UserProfile has one main optional (nullable) Blog"
So ultimately I'd like to see a FK from Blog.UserId to UserProfile.UserId
And a nullable FK from UserProfile.BlogId to Blog.Id
And I think that is all... I especially don't want additional columns added by EF.
public class UserProfile
{
[Key]
public int UserId { get; set; }
public int? BlogId { get; set; }
public virtual Blog Blog { get; set; } // This is a user's main blog
public virtual ICollection<Blog> AllUsersBlogs { get; set; }
}
public abstract class Blog
{
[Key]
public int Id { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile User { get; set; }
}

That's pretty tricky thing to make - by default CF puts all the relationships / FK-s on one side. And that's for a reason, because it simplifies things, avoid cyclical references and contradicting 'constraints' on two sides
what often happens is the error reporting that from one FK ir requires
to be of multiplicity '1' and from the other FK it has to be * -
resulting in an exception.
But this works all you want I think - you just have to 'feed it' the data carefully...
public class UserProfile
{
[Key]
public int UserId { get; set; }
[ForeignKey("Blog")]
public int? BlogId { get; set; }
public virtual Blog Blog { get; set; } // This is a user's main blog
public virtual ICollection<Blog> AllUsersBlogs { get; set; }
}
//abstract
public class Blog
{
[Key]
public int Id { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
// [ForeignKey("UserId")]
public virtual UserProfile User { get; set; }
}
In your fluent config...
modelBuilder.Entity<Blog>()
.HasRequired(x => x.User)
.WithMany(x => x.AllUsersBlogs)
.HasForeignKey(x => x.UserId)
.WillCascadeOnDelete(false);
And use it like this...
var user = db.UserProfiles.Add(new UserProfile
{
//Blog = mainblog,
AllUsersBlogs = new List<Blog>
{
new Blog{},
new Blog{},
new Blog{},
new Blog{},
new Blog{},
}
});
db.SaveChanges();
var mainblog = new Blog { User = user, };
user.Blog = mainblog;
db.SaveChanges();
Note that for the main blog - you have to explicitly specify the User for your blog now - plus set it as the user's main blog.
That's because you have two different relationships now - one is mandatory (User in the Blog) - and another is the optional main blog.
Anyhow, if this doesn't satisfy your demands (though it looks it
should I think) - then I'd suggest that you let it create things by
default and have FK-s on the Blog side, you lose the BlogId but it
simplifies things a lot.

Related

Entity Framework Core error occures when trying to include other entity

I am working with some entities which are connected by foreign keys using the Entity Framework code-first approach. When I try to include one from another I get an error that says:
The expression 'user.Organization' is invalid inside an 'Include' operation, since it does not represent a property access
These are my classes:
public class User : Person
{
public StaffRole? Role { get; set; } = null;
[ForeignKey(nameof(Organization))]
public Guid? OrganizationId { get; set; }
[NotMapped]
public virtual Organization Organization { get; set; }
}
public class Organization : Auditable
{
public Organization()
{
Staffs = new List<User>();
}
[Required]
public string Name { get; set; }
public virtual ICollection<User> Staffs { get; set; }
}
I have removed some properties in order to be clear.
This is how I am trying to include
var owner = userRepository.GetAll(user => user.Id == currentUser.Id &&
user.Role == StaffRole.Owner).Include(user => user.Organization).FirstOrDefault();
I have looked around the Web to find the answer but I think I need a little bit of individual help.

EF6 automatically includes nested objects

Probably a simple question, but there is something I can't get my head around.
My structure Bundle -> BundleMembers -> InsuranceTypes
When I retrieve a single record form BundleMembers, and I include Bundle. EF6 automatically includes all BundleMembers in the Bundle
Example:
public async Task<List<BundleMember>> GetBundleMembers(string userId, bool includeBundle, bool includeInsuranceTypes)
{
var bundleMembers = db.BundleMembers.Where(m => string.Equals(m.UserId, userId, StringComparison.CurrentCultureIgnoreCase));
if (includeBundle)
{
bundleMembers = bundleMembers.Include(o => o.Bundle);
}
if (includeInsuranceTypes)
{
bundleMembers = bundleMembers.Include(m => m.BundleMemberInsuranceType);
}
return await bundleMembers.ToListAsync();
}
I call the function like this:
GetBundleMembers(_userManager.GetUserId(User), true, false)
Do I have to access the data from Bundle, to avoid this?
EDIT 1:
My data model looks like this:
public class Bundle
{
public int BundleId { get; set; }
public State State { get; set; }
public ICollection<BundleMember> Members { get; set; }
public ICollection<InviteLink> InviteLinks { get; set; }
public string BundleName { get; set; }
public string Description { get; set; }
public string ImagePath { get; set; }
}
public enum State
{
NotApproved,
Approved,
Disabled,
Rejected
}
public class BundleMember
{
public ApplicationUser User { get; set; }
public string UserId { get; set; }
public int BundleMemberId { get; set; }
public int BundleId { get; set; }
public Bundle Bundle { get; set; }
public bool Admin { get; set; }
public int Price { get; set; }
public int Coverage { get; set; }
public ICollection<BundleMemberInsuranceType> BundleMemberInsuranceType { get; set; }
}
I did not include BundleMemberInsuranceType and InviteLink as they are working fine.
Relevant part of ApplicationDbContext:
public DbSet<Bundle> Bundles { get; set; }
public DbSet<BundleMember> BundleMembers { get; set; }
As suggested in comments:
The described behavior is actually expected. Since includeBundle is set to true, both Bundles and referenced BundleMembers are in the context, and relationship fixup will set all navigation properties according to the FK relationships.
Obviously, this works both from BundleMembers to Bundles and from Bundles to BundleMembers since .Include does nothing more than create the SQL statements to load the related entries into the context as well and relationship fixup will do the rest.
To have the Bundles not have BundleMembers, you'll have to load them without the BundleMembers in the context and set the navigation properties yourself (EF will always set both direct and inverse navigation properties). In order to do this, there are two main ways:
Either load your bundles in a fresh context without the previous loaded BundleMembers (best practice is to load them into memory since EF navigation properties are loaded due to eager loading; you could have entries attached to two contexts and an exception will be thrown) or
Detach your BundleMembers from the context before loading the Bundles into it.

Entity Framework shows inconsistent behaviour when used with Asp.net Identity

I have 3 tables Violation,Comment and and auto generated AspNetUsers respectively.The relationship between them as follows.
I am using code-first approach and my models are as follows.Some properties are removed for brevity.
Violation Model
public class Violation
{
public Violation()
{
this.Comments = new HashSet<Comment>();
}
public int Id { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ApplicationUser CreatorUser { get; set; }
}
Comment Model
public class Comment
{
public int Id { get; set; }
[Required]
public string Content { get; set; }
[Required]
public DateTime PostedDateTime { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public Violation Violation { get; set; }
}
ApplicationUser(AspNetUsers Table)
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
this.Comments = new List<Comment>();
this.Violations = new List<Violation>();
}
public virtual List<Comment> Comments { get; set; }
public virtual List<Violation> Violations { get; set; }
}
The problem is that when I try to retrieve Comment's ApplicationUser navigation property , I see many of them pointing to a null property even database has proper record for each of them.
Shortly,EF doesn't retrieve database records properly.I stuck with it,can't find the reason.
In fact, it's not being lazy-loaded. You didn't add the virtual keyword to your Comment.ApplicationUser property, so Entity Framework cannot override it to add the lazy-loading logic. As a result, it's always going to be null unless you explicitly load it. Add the virtual keyword, and you'll be fine.
If you want the navigation properties populated you need to include them in the query:
var comments = context.Comments
.Include(c => c.Violation)
.Include(c => c.ApplicationUser)
.Where(x => x.Violation.Id == violationId);
https://msdn.microsoft.com/en-us/data/jj574232.aspx#eager

How can I create two types of users in MVC5?

I'm creating MVC5 app, and I'm already using ASP.NET Identity to create users. So, I already have the AspNetUsers table, and whenever user registers I get an entry there. I also have an Admin role, where I manually specify, which registered user is an admin. On the other hand, I also need to register Businesses, and much like normal Users, they will be able to log-in, register, and do some stuff. The point is that they will have both some similar and different fields with/from the normal users. For example, they will also have, e-mail address, password (which I want to be hashed like for normal users), e-mail confirmation, unique id etc. But they have different fields for more information, like their address, zip, country, category, etc. which normal users don't have. How can I achieve this in MVC?
Should I do something like the ApplicationUser class?
public class ApplicationUser : IdentityUser
I mean, should I inherit my Business model from the IdendityUser? If yes, how will my model know which of the fields from IdentityUser to use and which not?
Here is my current Business model:
public class Business
{
public int BusinessID { get; set; }
public string BusinessName { get; set; }
[ForeignKey("Category")]
public int CategoryID { get; set; }
public virtual Category Category { get; set; }
[ForeignKey("Subcategory")]
public int SubcategoryID { get; set; }
public virtual Subcategory Subcategory { get; set; }
public string BusinessAddress { get; set; }
public string BusinessZip { get; set; }
public string BusinessPhone { get; set; }
public string BusinessDescription { get; set; }
public string Facebook { get; set; }
public string Twitter { get; set; }
public byte[] ImageData { get; set; }
public string ImageMimeType { get; set; }
[Range(0.0, 5.0)]
public double BusinessRating { get; set; }
public virtual ICollection<Review> Reviews { get; set; }
}
So, apart from those fields, I want my table to include the stuff similar to AspNetUsers, like Email, EmailConfirmed, PasswordHash, SecurityStamp, etc.
EDIT:
Please note that some of my fields in the Business model are required. And also below you can find my ApplicationUser class.
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
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;
}
}
Use simple inheritance:
public class Business : ApplicationUser
{
...
}
You'll end up with a Discriminator column in your AspNetUsers table that will help Entity Framework identity which class it should instantiate for the row (Business or ApplicationUser). Then you can either just query as normal or if you only want one particular type or another, you can use OfType<T>:
var businessUsers = db.Users.OfType<Business>();
Note: By default, Entity Framework handles simple inheritance with a single table with a Discriminator column. For most cases this works just fine, but you must keep in mind that any property you add to subclasses of your base class, must be nullable. You cannot require something like a DateTime on Business to be required at the database-level, because then you could never save an ApplicationUser, which does not that property. However, this is only an issue at the database-level. You can still use view models to make a particular property on Business required from a front-end perspective.

entity framework multiple relationships

I'm new to Entity Framework and I have probably a simple question.
I have simplified the structure at a maximum to be clear (I hope I am).
Imagine that I just need to create a simple "Enterprise" class, with only a Name.
Then another class named "Worker" with also just a Name for the worker.
A worker should belong to an Enterprise.
An Enterprise must have a manager (who is a Worker).
So here is how I imagine these simple classes :
public class Worker
{
public int WorkerId { get; set; }
public String Name { get; set; }
public int EnterpriseId { get; set; } // ForeignKey for Enterprise
public Enterprise Enterprise { get; set; }
}
public class Enterprise
{
public int EnterpriseId { get; set; }
public String Name { get; set; }
public Worker Manager { get; set; }
public List<Worker> Workers { get; set; }
}
I'd like these classes to result in the following DB structure :
Table Worker
WorkerId (PK, int, not null)
Name (varchar(128), not null)
EnterpriseId (FK, int)
Table Enterprise
EnterpriseId (PK, int, not null)
Name (varchar(128), not null)
Manager (FK, int)
I tried many things with modelBuilder, but I never obtain what I want.
Is there a solution with Fluent API to do what I want to do?
Thank you very much for your help.
This will not get you what you want (in Db) - but is what I recommend...
public ICollection<Worker> Workers { get; set; } // instead of List<>
// ...
modelBuilder.Entity<Worker>()
.HasRequired(x => x.Enterprise)
.WithMany(x => x.Workers)
.HasForeignKey(x => x.EnterpriseId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Enterprise>()
.HasOptional(x => x.Manager)
.WithOptionalPrincipal() // x => x.DefaultForEntity)
.WillCascadeOnDelete(false);
You can use it like:
var enterprise = new Enterprise { Manager = new Worker { Name = "Manager", }, };
enterprise.Workers = new[]
{
enterprise.Manager,
new Worker{ Name = "Worker1", },
new Worker{ Name = "Worker2", },
new Worker{ Name = "Worker3", },
new Worker{ Name = "Worker4", },
new Worker{ Name = "Worker5", },
};
db.Enterprises.Add(enterprise);
db.SaveChanges();
var enterprises = db.Enterprises.ToList();
This is exactly what you want...
modelBuilder.Entity<Worker>()
.HasRequired(x => x.Enterprise)
.WithMany(x => x.Workers)
.HasForeignKey(x => x.EnterpriseId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Enterprise>()
.HasKey(x => x.EnterpriseId);
modelBuilder.Entity<Enterprise>()
.HasOptional(x => x.Manager)
.WithOptionalDependent() // x => x.DefaultForEntity)
.WillCascadeOnDelete(false);
...but will not work - due to cyclical references (EF error).
Here is a pretty detailed example for a similar / identical solution...
Entity Framework One-to-Many with Default
I don't know how you intend to get the manager object, but my guess is you need to use Inheritance to make your design optimal. Try this:
public abstract class Employee
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EmployeeId{ get; set; }
public String Name { get; set; }
[ForeignKey("Enterprise"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public int EnterpriseId { get; set; } // ForeignKey for Enterprise
public Enterprise Enterprise { get; set; }
}
[Table("Workers")] // Table per Type (TPT), This will be your Table name in your database
public class Worker : Employee
{
//Add properties only related to workers
}
[Table("Managers")] // Table per Type (TPT). This will be your Table name in your database
public class Manager : Employee
{
//Add properties only related to Managers
}
public class Enterprise
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EnterpriseId { get; set; }
public String Name { get; set; }
public virtual ICollection<Employee> Employees{ get; set; }
}
Note: Sorry this is done using Property Mapping
Link: Here is a link to simple Fluent Mapping example
Link : Read about Table per Type (TPT) Inheritance here

Resources