MVC5 codefirst adding a user to many-many relationship - entity-framework

Im using mvc5 with codefirst and the user account stuff.
I have a simple requirement a user can belong to multiple stores and a business can have multiple stores.
So far I have the business - many stores working but I cant seem to figure out how to setup the users.
My StoreModel
public virtual Business Business { get; set; }
public virtual ICollection<ApplicationUser> Employees { get; set; }
My BusinessModel
public virtual ICollection<StoreModel> Stores { get; set; }
Within my context I have tried
public class ApplicationUser : IdentityUser
{
//a user can belong to multiple stores
public virtual ICollection<StoreModel> Stores { get; set; }
}
however when I try add-migration the code generated is going to change my table names and does create the join table between Store & AspNetUser
My migration looks like
public override void Up()
{
RenameTable(name: "dbo.Stores", newName: "ApplicationUserStoreModels");
DropForeignKey("dbo.Stores", "ApplicationUser_Id", "dbo.AspNetUsers");
DropIndex("dbo.Stores", new[] { "ApplicationUser_Id" });
CreateIndex("dbo.ApplicationUserStoreModels", "ApplicationUser_Id");
CreateIndex("dbo.ApplicationUserStoreModels", "StoreModel_StoreId");
AddForeignKey("dbo.ApplicationUserStoreModels", "ApplicationUser_Id", "dbo.AspNetUsers", "Id", cascadeDelete: true);
AddForeignKey("dbo.ApplicationUserStoreModels", "StoreModel_StoreId", "dbo.Stores", "StoreId", cascadeDelete: true);
DropColumn("dbo.Stores", "ApplicationUser_Id");
}
Can anyone help what I need to do to get this working?

you can either just not have a store table and put a list in each appuser and business and EF will make your many to many relationship or you can use fluent api to map everything. Should be something like the following.
public class Business
{
public virtual ICollection<StoreModel> Stores { get; set; }
}
public class ApplicationUser : IdentityUser
{
public virtual ICollection<StoreModel> Stores { get; set; }
}
public class StoreModel {
public string ApplicationUserId { get; set; }
public int BusinessId { get; set; }
public virtual Business Businesss { get; set; }
public virtual ApplicationUser ApplicationUsers { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ base.OnModelCreating(modelBuilder);
modelBuilder.Entity<StoreModel>().HasKey(e => new { e.ApplicationUserId, e.BusinessId});
}

Related

Link models from different databases

I have the following situation: I have a class Company, it is as simple as possible
public class Company
{
public int ID { get; set; }
public string Name { get; set; }
}
I also need to extend the IdentityUser class from ASP. NET Identity Core:
public class User : IdentityUser
{
public Company Company { get; set; }
public int CompanyID { get; set; }
}
Not bad so far. But I also need a class Device :
public class Device
{
public Company Company { get; set; }
public int CompanyID { get; set; }
}
Accordingly, we add the Devices property of the List<Device> type to the Company class and Users property of the List<User> type.
The problem is that I want to separate the data (Device table) from the data of ASP .NET Identity Core in different databases. Table Company I want to put in the database with Auth data. And table Device in other one. How can I tell the Entity Framework that the Company property of the Device class is from another database?
If the Company property is from another database, you may need to add a second database context for it, if you haven't already. Something like this:
public class CompanyModelContext : DbContext
{
public CompanyModelContext(DbContextOptions options) : base(options){ }
public DbSet<Company> Company { get; set; }
}
You can try to set your EF model like this:
Companies table:
[Table("Companies")]
public class Company
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Device> Devices { get; set; }
}
Devices table:
[Table("Devices")]
public class Device
{
[Key]
public int Id { get; set; }
public Company Company { get; set; }
public int CompanyID { get; set; }
}
ApplicationDbContext class:
public DbSet<Company> Companies { get; set; }
public DbSet<Device> Devices { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Company>(buildAction =>
{
buildAction.ToTable("Companies");
buildAction.HasMany(x => x.Devices)
.WithOne(x => x.Company)
.OnDelete(DeleteBehavior.Cascade);
};
}
I've added Devices as a list in Company class, then overriding the FK in the method OnModelCreating.
Once you delete an item in table Company, all items with the same Id in table Devices (comparing to CompanyID) will be delted automatically.
Since we use the 2 properties [Table] and [Key] to define table name and the primary key. So in the OnModelCreating, you can ignore to define the name, PK, FK of table Device.
Company has many User
In this case:
User class:
public class User : IdentityUser
{
public int CompanyId { get; set; }
public Company Company { get; set; }
}
Company class:
[Table("Companies")]
public class Company
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Device> Devices { get; set; }
public ICollection<User> Users { get; set; }
}
ApplicationDbContext class:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Company>(buildAction =>
{
buildAction.ToTable("Companies");
buildAction.HasMany(x => x.Devices)
.WithOne(x => x.Company)
.OnDelete(DeleteBehavior.Cascade);
buildAction.HasMany(x => x.Users)
.WithOne(x => x.Company)
.OnDelete(DeleteBehavior.Cascade);
};
}

EF code first telling me to do the migration for db object which is already is in db

i am working with EF code first. so initially i have no tables in database. so i wrote some class and when query those class then i saw EF code first create those tables in db but when i create sql server view in db and later map that view with my code in c# & EF project and when i try to query that view then i was getting error message as follows.
Additional information: The model backing the 'TestDBContext' context has changed since the database was created. Consider using Code First Migrations to update the database
i understand that EF is telling me to do the migration but if i migrate then EF will create that view in db again when the view is in db already exist.
so tell me how could i inform EF that my view is already is in db so migration is not required.
please guide me. thanks
EDIT 1
first time my database has no table. so i wrote some classes like below one.
public class CustomerBase
{
public int CustomerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
}
public class Customer : CustomerBase
{
public virtual List<Addresses> Addresses { get; set; }
}
public class Addresses
{
[Key]
public int AddressID { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public bool IsDefault { get; set; }
public virtual List<Contacts> Contacts { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
}
public class Contacts
{
[Key]
public int ContactID { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
public bool IsDefault { get; set; }
public int AddressID { get; set; }
public virtual Addresses Customer { get; set; }
}
public class TestDBContext : DbContext
{
public TestDBContext()
: base("name=TestDBContext")
{
}
public DbSet<Customer> Customer { get; set; }
public DbSet<Addresses> Addresses { get; set; }
public DbSet<Contacts> Contacts { get; set; }
}
when i query the customer like below query then EF create all required tables in db behind the curtains.
var bsCustomer = (from cu in db.Customer
where (cu.CustomerID == 2)
select new
{
cu,
Addresses = from ad in cu.Addresses
where (ad.IsDefault == true)
from ct in ad.Contacts
select ad,
}).ToList();
later i create a view in db and refer that view in code like below one.
public partial class vwCustomer
{
[Key]
public int CustomerID { get; set; }
public string FirstName { get; set; }
}
public class vwCustomerConfiguration : EntityTypeConfiguration<vwCustomer>
{
public vwCustomerConfiguration()
{
this.HasKey(t => t.CustomerID);
this.ToTable("vwCustomers");
}
}
so now my DbContext look like below one with view class reference
public class TestDBContext : DbContext
{
public TestDBContext()
: base("name=TestDBContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new vwCustomerConfiguration());
}
public DbSet<Customer> Customer { get; set; }
public DbSet<Addresses> Addresses { get; set; }
public DbSet<Contacts> Contacts { get; set; }
public virtual DbSet<vwCustomer> vwCustomers { get; set; }
}
Error occur the moment i try to query the view
using (var db = new TestDBContext())
{
var listMyViews = db.vwCustomers.ToList();
}
the error was Additional information: The model backing the 'TestDBContext' context has changed since the database was created. Consider using Code First Migrations to update the database
thanks
Another way we can do it and it solve my problem. see the code.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<YourDbContext>(null);
base.OnModelCreating(modelBuilder);
}
code taken from here https://stackoverflow.com/a/6143116/6188148
we can follow this approach too.
public partial class AddingvwCustomer : DbMigration
{
public override void Up()
{
}
public override void Down()
{
}
}
i guess this will works too but not tested myself.
we can use the Fluent API to configure it using the Ignore method:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<MyClass>();
}
Add new migration as normally and from the migration code in Up (and Down) method remove code that tries to create new table manually (call to CreateTable method in Up and DropTable in Down). Then apply migration to your db and everything works perfectly.
Unfortunately automatic migration generation is not very intelligent tool and very often one need to manually specify how the database should be altered. In the documentation for EF migrations it is stated that it is perfectly fine to edit manually migrations code.

Navigation Properties using two foreign keys

I have a project with several tables in the same database.
public class UserImage
{
[Key]
public int Id { get; set; }
public string OwnerId { get; set; }
[ForeignKey("OwnerId")]
public virtual ApplicationUser Owner { get; set; }
//some fields are removed for brevity
}
public class FriendRequest
{
[Key]
public int Id { get; set; }
public string UserId { get; set; }
public string FutureFriendUserId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser User { get; set; }
[ForeignKey("FutureFriendUserId")]
public virtual ApplicationUser FutureFriendUser { get; set; }
}
public class ApplicationUser : IdentityUser
{
//some fields are removed for brevity
public virtual ICollection<UserImage> UserImages { get; set; }
public virtual ICollection<FriendRequest> FriendRequests { get; set; }
The problem is that I can find the images that belong to a user:
var userStore = new UserStore<ApplicationUser>(db);
var userManager = new UserManager<ApplicationUser>(userStore);
ApplicationUser user = userManager.FindById(User.Identity.GetUserId());
IEnumerable<string> imgs = (from image in user.UserImages select Url.Content(image.ImageUrl)).Skip(skip).Take(5).ToList();
but I can't use the same technique for the FriendRequests. If I search in the database for the rows that have UserId == User.Identity.GetUserId() or some other id, the results are what I expect.
What is the problem?
What you're essentially creating here is a self-referential many-to-many relationship. On your FriendRequest class, you have two properties that are foreign keys to ApplicationUser, but on your ApplicationUser class, you have only a single collection of FriendRequest. Entity Framework has no idea which foreign key should actually compose this collection. As a result, you have to make a few changes to get this working properly.
You must add another navigation property. Essentially, on your ApplicationUser class you'll end up with something like the following:
public virtual ICollection<FriendRequest> SentFriendRequests { get; set; }
public virtual ICollection<FriendRequest> ReceivedFriendRequests { get; set; }
Again, you need a collection for each foreign key.
You'll need to add some fluent config to help Entity Framework determine which foreign key to use for each collection:
public class ApplicationContext : DbContext
{
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<FriendRequest>().HasRequired(m => m.User).WithMany(m => m.SentFriendRequests);
modelBuilder.Entity<FriendRequest>().HasRequired(m => m.FutureFriendUser).WithMany(m => m.ReceivedFriendRequests);
}

EF codefirst : How do i make an 1:many (uniDirectional)relation cascade on delete

Here is my models :
public class User
{
public int UserId { get; set; }
public virtual ICollection<Shipment> Shipments { get; set; }
}
public class Shipment
{
public int ShipmentId { get; set; }
public int UserId {get; set;}
}
Any User May have 0~many shipments.I want to casccade the corresponded shipments and delete them automaticly when i delete an user.
I dont want make the relation BiDirrectional.and eneable DeleteOnCascade from other side by make the Virtual User Property [Requierd].
thanks.
You can achieve the above by overriding 'OnModelCreating' as follows.
public class DataContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(u => u.Shipments).WithOptional().WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
}

EF4.1 How to I implement a CascadeDelete where two tables share records in one table?

I'm using EF4.1 Code First. I have two classes which both have a one-to-one relationship with a contact class. When I remove a record in either of the two classes I want the associated entry in the contact class removed also.
ex:
public class User
{
public virtual int ID { get; set; }
...
public virtual Contact Contact { get; set; }
}
public class Admin
{
public virtual int ID { get; set; }
...
public virtual Contact Contact { get; set; }
}
public class Contact
{
public virtual int ID { get; set; }
...
}
I tried various things with annotations and fluent API but could not yet manage to get a cascade delete working. What is the correct way to implement this in EF 4.1 Code First?
I'm not sure but I think your contact entity needs User and Admin entities also..
Then the fluent api should work:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasRequired(u => u.Contact)
.WithRequiredPrincipal(c => c.User)
.WillCascadeOnDelete();
modelBuilder.Entity<Admin>()
.HasRequired(a => a.Contact)
.WithRequiredPrincipal(c => c.Admin)
.WillCascadeOnDelete();
}
I think this should work if you want to delete Users and Admins if a Contact is deleted:
public class User
{
public int ID { get; set; }
public int ContactId { get; set; }
public virtual Contact Contact { get; set; }
}
public class Contact
{
public int ID { get; set; }
public virtual List<User> Users {get; set;}
public Contact()
{
Users = new List<User>();
}
}
While I believe you want to do the opposite, which means you need to make the Contact is the dependent entity
public class User
{
public int ID { get; set; }
public bool IsAdmin {get; set;}
public virtual List<Contact> {get; set;}
public Contact()
{
Users = new List<User>();
}
}
public class Contact
{
public int ID { get; set; }
[ForiegnKey("Owner")]
public int UserId {get; set;}
public virtual User Owner {get; set;}
}
you can use other types of Inheritance other than TPC I'm using here, because DBMS doesn't support two mutually exclusive Foreign Keys