This is my simplified schema.
I want to add a contact who is a custodian and attach them to an existing facility. The ContactType just states what type of contact the record is such as a company or individual. In this case the custodian is an individual.
I can insert a contact by itself just fine.
The complication for me is because of the many to many and one to many relationships.
In the controller I have
vm.Contact.Facilities.Add(new Facility { FacilityID = vm.SelectedFacilityID });
_repo.SaveContact(vm);
In the repo
_db.Contacts.Add(vm.Contact);
_db.SaveChanges();
This gives me a foreign key error since it tries to insert a new facility and I have other foreign keys in that table not shown. I don't want to add a facility but just reference the FacilityID.
Convention is to Not explicitly try to insert a record in the many to many, FacilityCustodian table directly by doing something like this
var fc = new FacilityCustodian { CustodianFacilityID = vm.SelectedFacilityID };
vm.Contact.FacilityCustodian.Add(fc);
I also tried
foreach (var facility in vm.Contact.Facilities)
{
_db.Entry(facility).State = EntityState.Unchanged;
}
Most of the examples I have seen don't have both relationships so I'm not sure how to proceed.
Thanks for any help.
ADDED CODE
using Licensing.Models;
namespace Licensing
{
using System.Data.Entity;
public class Context : DbContext
{
public Context()
: base("name=Context")
{
}
public virtual DbSet<Contact> Contacts { get; set; }
public virtual DbSet<ContactType> ContactTypes { get; set; }
public virtual DbSet<Facility> Facilities { get; set; }
public virtual DbSet<FacilityCandler> FacilityCustodians { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>()
.Property(e => e.FName)
.IsUnicode(false);
modelBuilder.Entity<Contact>()
.Property(e => e.LName)
.IsUnicode(false);
modelBuilder.Entity<Contact>()
.HasMany(e => e.Facilities)
.WithRequired(e => e.Contact)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Contact>()
.HasMany(e => e.FacilityCustodians)
.WithRequired(e => e.Contact)
.HasForeignKey(e => e.CustodianFacilityID)
.WillCascadeOnDelete(false);
modelBuilder.Entity<ContactType>()
.Property(e => e.Type)
.IsUnicode(false);
modelBuilder.Entity<ContactType>()
.HasMany(e => e.Contacts)
.WithRequired(e => e.ContactType)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Facility>()
.HasMany(e => e.FacilityCustodians)
.WithRequired(e => e.Facility)
.HasForeignKey(e => e.CustodianFacilityID)
.WillCascadeOnDelete(false);
}
}
}
namespace Licensing.Models
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
[Table("Contact")]
public class Contact
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Contact()
{
Facilities = new HashSet<Facility>();
FacilityCustodians = new HashSet<FacilityCustodian>();
}
[Key]
public int ContactID { get; set; }
public int ContactTypeID { get; set; }
[Display(Name = "First Name")]
[RegularExpression(#"^[a-zA-Z'.\s]+$", ErrorMessage = "Enter a valid Name")]
[StringLength(150)]
public string FName { get; set; }
[Display(Name = "Last Name")]
[RegularExpression(#"^[a-zA-Z'\s]+$", ErrorMessage = "Enter a valid Name")]
[StringLength(150)]
public string LName { get; set; }
public virtual ContactType ContactType { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Facility> Facilities { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<FacilityCustodian> FacilityCustodians { get; set; }
}
}
using System.ComponentModel.DataAnnotations;
namespace Licensing.Models
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
[Table("Facility")]
public class Facility
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Facility()
{
FacilityCustodians = new HashSet<FacilityCustodian>();
}
[Key]
public int FacilityID { get; set; }
public int ContactID { get; set; }
public virtual Contact Contact { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<FacilityCustodian> FacilityCustodians { get; set; }
}
}
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Licensing.Models
{
[Table("FacilityCustodian")]
public partial class FacilityCustodian
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CustodianFacilityID { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CustodianContactID { get; set; }
public virtual Contact Contact { get; set; }
public virtual Facility Facility { get; set; }
}
}
namespace Licensing.Models
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("ContactType")]
public class ContactType
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public ContactType()
{
Contacts = new HashSet<Contact>();
}
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ContactTypeID { get; set; }
[Required]
[StringLength(10)]
public string Type { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Contact> Contacts { get; set; }
}
}
UPDATE Working Code
var existingFacility = _repo.GetFacilityByFacilityID(vm.SelectedFacilityID);
vm.Contact.LName = vm.LName;
vm.Contact.FName = vm.FName;
vm.Contact.Name = vm.FName + " " + vm.LName;
vm.Contact.ContactTypeID = 1;
vm.Contact.FacilityCustodians.Add(existingFacility);
_db.Contacts.Add(vm.Contact);
_db.SaveChanges();
Try this Since I dont have complete folder I tried to simplify the model all classes are name mentioned in diagram but I create. Below are my model classes . The import thing to note is that read the entoty from contecxt first and then updated the values and set entitystate accordingly . This will solve your issue .
Model classes :-
public class ContactType
{
public int ContactTypeId { get; set; }
public string Name { get; set; }
}
public class Contact
{
public int ContactId { get; set; }
public string Name { get; set; }
public int ContactTypeId { get; set; }
[ForeignKey("ContactTypeId")]
public virtual ContactType ContactType { get; set; }
public virtual ICollection<Facility> Facilities { get; set; }
}
public class FacilityCustodian
{
public int FacilityId { get; set; }
public int ContactTypeId { get; set; }
[ForeignKey("ContactId")]
public virtual Contact Contact { get; set; }
[ForeignKey("FacilityId")]
public virtual
Facility Facility { get; set; }
}
public class Facility
{
public int FacilityId { get; set; }
public string Location { get; set; }
public int ContactId { get; set; }
[ForeignKey("ContactId")]
public virtual Contact Contact { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
-- here I am showing you to update existing contact and add a new facility to it . same as you case you want to update facility ...
using (var ctx = new SampleDbContext())
{
//this is important to read the entity first .
var contact = ctx.Contacts.FirstOrDefault(x=>x.ContactId ==vm.Contact.ContactId);
if (contact != null)
{
// Since facilityId is a primary key I set it to autoincrement so I dont have to set it .You can set if it is not primarykey
contact.Name = "Updated";
// update the entity and add new information inthis i am adding facility
var facility = new Facility
{
Location = "LocA",
// assiging same entity to facility so that it will not treat it as a new contact
Contact = contact
};
contact.Facilities.Add(facility);
// Finaly update the state of the entity .
ctx.Entry(contact).State = EntityState.Modified;
ctx.SaveChanges();
}
}
After this it will not insert new contact when you save in . In my example I choose to add new facility but keep contact same . You can do same with facility but the concept is same . First read the object from EF coontext update values which you need to update and set state to Modified . This will tell that all other Entities in graph are same expect which you modified.
SQL Profiler Query to make sure Update happn for Contact and insert for facility
exec sp_executesql N'UPDATE [dbo].[Contacts]
SET [Name] = #0, [ContactTypeId] = #1
WHERE ([ContactId] = #2)
',N'#0 nvarchar(max) ,#1 int,#2 int',#0=N'Updated',#1=1,#2=1
go
exec sp_executesql N'INSERT [dbo].[Facilities]([Location], [ContactId], [Contact_ContactId])
VALUES (#0, #1, #2)
SELECT [FacilityId]
FROM [dbo].[Facilities]
WHERE ##ROWCOUNT > 0 AND [FacilityId] = scope_identity()',N'#0nvarchar(max) ,#1 int,#2 int',#0=N'LocA',#1=1,#2=1
go
Related
Answer to this question is found at here
Having three tables:
Database diagram is here
Book class:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
public partial class Books
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Books()
{
UserBookComments = new HashSet<UserBookComments>();
}
[Key]
public int BookID { get; set; }
[Required]
[StringLength(255)]
public string Title { get; set; }
[Required]
[StringLength(255)]
public string Category { get; set; }
[Column(TypeName = "date")]
public DateTime PublishDate { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<UserBookComments> UserBookComments { get; set; }
}
User class:
public partial class Users
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Users()
{
UserBookComments = new HashSet<UserBookComments>();
}
[Key]
public int UserID { get; set; }
[Required]
[StringLength(255)]
public string UserName { get; set; }
[Required]
[StringLength(255)]
public string Password { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<UserBookComments> UserBookComments { get; set; }
}
And the UserBookComments class:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
public partial class UserBookComments
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserID { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int BookID { get; set; }
public int? Comments { get; set; }
public virtual Bookss Bookss { get; set; }
public virtual Users Users { get; set; }
}
The table "Books" is an already saved database. Each user can comment for each book and I want a view model that holds all the data from books with their comments.
The primary key on UserBookComment would be composite, on UserID and BookID.
I used EF Code First and my DBModel context class looks so:
using System.Data.Entity;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
public partial class DbModel : DbContext
{
public DbModel()
: base("name=DbModel")
{
}
public virtual DbSet<Books> Books { get; set; }
public virtual DbSet<UserBookComments> UserBookComments { get; set; }
public virtual DbSet<Users> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Books>()
.Property(e => e.Category)
.IsUnicode(false);
modelBuilder.Entity<Books>()
.HasMany(e => e.UserBookComments)
.WithRequired(e => e.Books)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Users>()
.HasMany(e => e.UserBookComments)
.WithRequired(e => e.Users)
.WillCascadeOnDelete(false);
}
}
I wonder how to save comments and display whole list of [title, category, publish date and comments] using a ViewModel class?
As you asked in the comments, I have provided the way to insert a record into the UserBookComment table by adding a method into the BookEntities class.
public partial class BooksEntities : DbContext
{
public virtual DbSet<Books> Books { get; set; }
public virtual DbSet<UserBookComment> UserBookComments { get; set; }
public virtual DbSet<Users> Users { get; set; }
public void AddComment(int userId, int bookId, string comment)
{
var userBookComment = new UserBookComment()
{
UserId = userId,
BookId = bookId,
Comment = comment
};
this.AddComment(userBookComment);
}
public void AddComment(UserBookComment userBookComment)
{
this.UserBookComment.Add(userBookComment);
this.UserBookComment.SaveChanges();
}
}
I assumed based on your provided information that your UserBookComment class looked like this
public class UserBookComment
{
public int UserId { get; set; }
public int BookId { get; set; }
public string Comment { get; set; }
}
Looks like you are using entity framework. To create a composite key, just at the Key attribute on both properties. To control the key order use the column atribute. Referencing should be fixed automaticly using the names in the model, or try googling on entity framework foreign key. I'm sorry I don't have the link right now.
For the viewmodel, just don't foget to use include in the statement.
I'm using EF 6 and defining my database with Code First.
The following line of code returns a Transaction entity, however the EndorsementInfo navigation property is null. I've checked the database and there is definitely data for the test data. "var trans" does appear to have a valid IQueryable, but navigation property t.EndorsementInfo is null when it shouldn't be.
var trans = unitOfWork.GetRepository<Transaction>().GetAll().Where(t => t.PolicyId == command.PolicyId);
results.Transactions = new List<TransactionListItem>();
foreach (var t in trans)
{
results.Transactions.Add(new TransactionListItem
{
Id = t.Id,
EffDate = t.EffectiveDate,
EffectiveDate = t.EffectiveDate.ToShortDateString(),
TransactionType = t.TransactionType.ToStringValue(),
EndorsementType = t.TransactionType == TransactionType.Endorsement ?
t.EndorsementInfo.EndorsementType.Description : ""
});
}
Transaction Entity:
public class Transaction : EntityBase
{
[Required]
public TransactionType TransactionType { get; set; }
public long PolicyId { get; set; }
public virtual Policy Policy { get; set; }
[Required]
public DateTime EffectiveDate { get; set; }
public DateTime? ExpirationDate { get; set; }
public string Description { get; set; }
public virtual Quote QuoteInfo { get; set; }
public virtual Cancellation CancellationInfo { get; set; }
public virtual NewBusiness NewBusinessInfo { get; set; }
public virtual Endorsement EndorsementInfo { get; set; }
}
Endorsement Entity
public class Endorsement : EntityBase
{
public Transaction Transaction { get; set; }
public long EndorsementTypeId { get; set; }
public virtual EndorsementType EndorsementType { get; set; }
public int EndorsementNum { get; set; }
[MaxLength(500)]
public string EndorsementDesc { get; set; }
public Decimal? Premium { get; set; }
}
Code First Fluent Configurations
public class TransactionConfiguration : EntityTypeConfiguration<Transaction>
{
public TransactionConfiguration()
{
HasOptional(t => t.QuoteInfo).WithRequired(q => q.Transaction);
HasOptional(t => t.NewBusinessInfo).WithRequired(q => q.Transaction);
HasOptional(t => t.EndorsementInfo).WithRequired(q => q.Transaction);
HasOptional(t => t.CancellationInfo).WithRequired(q => q.Transaction);
}
}
Repositories implementation of GetAll
public IQueryable<T> GetAll(string include)
{
return _set.Include(include);
}
I've checked and rechecked that everything is set up correctly. I don't know what I could be missing.
Thanks.
You are using an opened connection to execute two data readers, you need to enable the multiple result set in the connection string.
MultipleActiveResultSets=True;
I'm trying to integrate the SimpleMembership tables with the rest of my Object Model - to manage all the entities from a single database and context.
Up to now the best recipe I've found for manually spinning up the SM tables (the entry point to combine SimpleMember with the rest of my Object Model) is found here. But, as cited in the comments section there are a couple mistakes in the code sample provided. The comments attempt to provide corrections but, due to formatted, really hard to follow.
I'm 80% the way there but getting stuck with the Foreign Key generation for the Membership table. Does the code within OnModelCreating block belong in the MyDbContext class? I'm getting a compile error on the .WithMany(u => u.Members) line.
Membership.cs
[Table("webpages_Membership")]
public class Membership
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
public DateTime? CreateDate { get; set; }
[StringLength(128)]
public string ConfirmationToken { get; set; }
public bool? IsConfirmed { get; set; }
public DateTime? LastPasswordFailureDate { get; set; }
public int PasswordFailuresSinceLastSuccess { get; set; }
[Required, StringLength(128)]
public string Password { get; set; }
public DateTime? PasswordChangedDate { get; set; }
[Required, StringLength(128)]
public string PasswordSalt { get; set; }
[StringLength(128)]
public string PasswordVerificationToken { get; set; }
public DateTime? PasswordVerificationTokenExpirationDate { get; set; }
<strike>public virtual ICollection<Role> Roles { get; set; }</strike>
EDIT: Originally I added the line above to remove a compiler complaint in the extraneous code block below. Removing this attempt to create a FK to Roles will align the rest of this code so that these model classes create a Migration that generates tables for SM.
OAuthMembership.cs
[Table("webpages_OAuthMembership")]
public class OAuthMembership
{
[Key, Column(Order = 0), StringLength(30)]
public string Provider { get; set; }
[Key, Column(Order = 1), StringLength(100)]
public string ProviderUserId { get; set; }
public int UserId { get; set; }
}
Role.cs
[Table("webpages_Roles")]
public class Role
{
[Key]
public int RoleId { get; set; }
[StringLength(256)]
public string RoleName { get; set; }
public virtual ICollection<UserProfile> UserProfiles { get; set; }
}
UserProfile.cs
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
MyDbContext.cs
public MyDbContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Membership> Membership { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<OAuthMembership> OAuthMembership { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<UserProfile>()
.HasMany<Role>(r => r.Roles)
.WithMany(u => u.UserProfiles)
.Map(m =>
{
m.ToTable("webpages_UsersInRoles");
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
EDIT: The block below was included in one of the article's comments but seems not to be needed.
//modelBuilder.Entity<Membership>()
// .HasMany<Role>(r => r.Roles)
// .WithMany(u => u.Members)
// .Map(m =>
// {
// m.ToTable("webpages_UsersInRoles");
// m.MapLeftKey("UserId");
// m.MapRightKey("RoleId");
// });
}
}
I followed the instructions in the article, and I also took into account the the comments that suggested the article was wrong in a few ways.
I ended up with the following classes:
UserProfile.cs
[Table("UserProfile")]
public class UserProfile
{
[Key, ForeignKey("Membership")]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public ICollection<WebSecurity.Role> Roles { get; set; }
public WebSecurity.Membership Membership { get; set; }
}
You should notice right away the "ForeignKey" attribute I use on the UserId column. Since the user is first created in the Membership table, my UserProfile table is the dependent table.
Membership.cs
[Table("webpages_Membership")]
public class Membership
{
//public Membership()
//{
// Roles = new List<Role>();
// OAuthMemberships = new List<OAuthMembership>();
//}
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
public DateTime? CreateDate { get; set; }
[StringLength(128)]
public string ConfirmationToken { get; set; }
public bool? IsConfirmed { get; set; }
public DateTime? LastPasswordFailureDate { get; set; }
public int PasswordFailuresSinceLastSuccess { get; set; }
[Required, StringLength(128)]
public string Password { get; set; }
public DateTime? PasswordChangedDate { get; set; }
[Required, StringLength(128)]
public string PasswordSalt { get; set; }
[StringLength(128)]
public string PasswordVerificationToken { get; set; }
public DateTime? PasswordVerificationTokenExpirationDate { get; set; }
public UserProfile UserProfile { get; set; }
}
Per Richard's comments in the article, I commented out the constructor. I also created a reference back to the UserProfile, but not to roles.
OAuthMembership.cs
[Table("webpages_OAuthMembership")]
public class OAuthMembership
{
[Key, Column(Order = 0), StringLength(30)]
public string Provider { get; set; }
[Key, Column(Order = 1), StringLength(100)]
public string ProviderUserId { get; set; }
public int UserId { get; set; }
//[Column("UserId"), InverseProperty("OAuthMemberships")]
//public Membership User { get; set; }
}
My OAuthMembership class remained basically the same; I commented out only the User attribute, per Richard's comment in the article.
AccountModel.cs+UsersContext
Finally, the UserContext class, where I create the association for the UsersInRoles table.
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<InternetApplication.Models.WebSecurity.Role>()
.HasMany<InternetApplication.Models.UserProfile>(r => r.UserProfiles)
.WithMany(u => u.Roles)
.Map(m =>
{
m.ToTable("webpages_UsersInRoles");
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
}
public DbSet<WebSecurity.Membership> Membership { get; set; }
public DbSet<WebSecurity.OAuthMembership> OAuthMembership { get; set; }
public DbSet<WebSecurity.Role> Roles { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
}
In addition to adding the UsersInRoles mapping, I added DbSet entries for each table.
Now that everything has been created, I can use my Add-Migration and Update-Database commands and use the following code snippet that combines the Membership, UserProfile, and Roles tables:
using (var db = new UsersContext())
{
var memberships = db.Membership
.Include("UserProfile")
.Include("UserProfile.Roles")
.ToList();
foreach (var member in memberships)
{
member.IsConfirmed = true;
}
db.SaveChanges();
}
This was a long post, but I hope that helps.
I used the answer to this question to automatically generate the models from the existing 'webpage_' tables in my database. This ensures that the models are created in the exact same way that SimpleMembership creates them. This resulted in the following code:
Models:
public partial class webpages_Membership
{
public int UserId { get; set; }
public Nullable<System.DateTime> CreateDate { get; set; }
public string ConfirmationToken { get; set; }
public Nullable<bool> IsConfirmed { get; set; }
public Nullable<System.DateTime> LastPasswordFailureDate { get; set; }
public int PasswordFailuresSinceLastSuccess { get; set; }
public string Password { get; set; }
public Nullable<System.DateTime> PasswordChangedDate { get; set; }
public string PasswordSalt { get; set; }
public string PasswordVerificationToken { get; set; }
public Nullable<System.DateTime> PasswordVerificationTokenExpirationDate { get; set; }
}
public partial class webpages_Roles
{
public webpages_Roles()
{
this.webpages_UsersInRoles = new HashSet<webpages_UsersInRoles>();
}
public int RoleId { get; set; }
public string RoleName { get; set; }
public virtual ICollection<webpages_UsersInRoles> webpages_UsersInRoles { get; set; }
}
public partial class webpages_UsersInRoles
{
public int UserId { get; set; }
public int RoleId { get; set; }
public virtual webpages_Roles webpages_Roles { get; set; }
}
Fluent Mappings:
internal partial class MembershipMapping : EntityTypeConfiguration<webpages_Membership>
{
public MembershipMapping()
{
this.HasKey(t => t.UserId);
this.ToTable("webpages_Membership");
this.Property(t => t.UserId).HasColumnName("UserId").HasDatabaseGeneratedOption(new Nullable<DatabaseGeneratedOption>(DatabaseGeneratedOption.None));
this.Property(t => t.CreateDate).HasColumnName("CreateDate");
this.Property(t => t.ConfirmationToken).HasColumnName("ConfirmationToken").HasMaxLength(128);
this.Property(t => t.IsConfirmed).HasColumnName("IsConfirmed");
this.Property(t => t.LastPasswordFailureDate).HasColumnName("LastPasswordFailureDate");
this.Property(t => t.PasswordFailuresSinceLastSuccess).HasColumnName("PasswordFailuresSinceLastSuccess");
this.Property(t => t.Password).HasColumnName("Password").IsRequired().HasMaxLength(128);
this.Property(t => t.PasswordChangedDate).HasColumnName("PasswordChangedDate");
this.Property(t => t.PasswordSalt).HasColumnName("PasswordSalt").IsRequired().HasMaxLength(128);
this.Property(t => t.PasswordVerificationToken).HasColumnName("PasswordVerificationToken").HasMaxLength(128);
this.Property(t => t.PasswordVerificationTokenExpirationDate).HasColumnName("PasswordVerificationTokenExpirationDate");
}
}
internal partial class RolesMapping : EntityTypeConfiguration<webpages_Roles>
{
public RolesMapping()
{
this.HasKey(t => t.RoleId);
this.ToTable("webpages_Roles");
this.Property(t => t.RoleId).HasColumnName("RoleId");
this.Property(t => t.RoleName).HasColumnName("RoleName").IsRequired().HasMaxLength(256);
}
}
internal partial class UsersInRolesMapping : EntityTypeConfiguration<webpages_UsersInRoles>
{
public UsersInRolesMapping()
{
this.HasKey(t => new { t.UserId, t.RoleId });
this.ToTable("webpages_UsersInRoles");
this.Property(t => t.UserId).HasColumnName("UserId").HasDatabaseGeneratedOption(new Nullable<DatabaseGeneratedOption>(DatabaseGeneratedOption.None));
this.Property(t => t.RoleId).HasColumnName("RoleId").HasDatabaseGeneratedOption(new Nullable<DatabaseGeneratedOption>(DatabaseGeneratedOption.None));
this.HasRequired(t => t.webpages_Roles).WithMany(t => t.webpages_UsersInRoles).HasForeignKey(d => d.RoleId);
}
}
Database Context:
public class MembershipContext : DbContext, IDisposable
{
public DbSet<webpages_Membership> Membership { get; set; }
public DbSet<webpages_Roles> Roles { get; set; }
public DbSet<webpages_UsersInRoles> UsersInRoles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MembershipMapping());
modelBuilder.Configurations.Add(new RolesMapping());
modelBuilder.Configurations.Add(new UsersInRolesMapping());
base.OnModelCreating(modelBuilder);
}
}
Note that I have excluded the OAuthMembership table, because I didn't need it for my solution. But if you follow the steps in the link I provided above you can easily include that table as well.
Starting from a blank MVC4 Internet Template I ran the project so as to create the SimpleMembership tables in a fresh db - then used EF's Reverse Engineer tool to create POCOs from those tables. Stepped thru it line by line to find the error and edited the code block in the OP.
With that code in place I used Package Manager to 'Add-Migration' and 'Update-Database'. Initial tests confirm everything works - I suppose I'll have to revisit if I find edge-cases that expose any deeper problems.
the sample code on http://msdn.microsoft.com/en-US/data/jj591620#RequiredToRequired is it even correct? the code is asking for OfficeAssignment prop on the Instructor class. it will not resolve for obvious reasons. what is the correct way of having a one-to-one relationship on ef now?
// Configure the primary key for the OfficeAssignment
modelBuilder.Entity<OfficeAssignment>()
.HasKey(t => t.InstructorID);
modelBuilder.Entity<Instructor>()
.HasRequired(t => t.OfficeAssignment)
.WithRequiredPrincipal(t => t.Instructor);
public class OfficeAssignment
{
// Specifying InstructorID as a primary
[Key()]
public Int32 InstructorID { get; set; }
public string Location { get; set; }
// When the Entity Framework sees Timestamp attribute
// it configures ConcurrencyCheck and DatabaseGeneratedPattern=Computed.
[Timestamp]
public Byte[] Timestamp { get; set; }
// Navigation property
public virtual Instructor Instructor { get; set; }
}
public class Instructor
{
public Instructor()
{
this.Courses = new List<Course>();
}
// Primary key
public int InstructorID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public System.DateTime HireDate { get; set; }
// Navigation properties
public virtual ICollection<Course> Courses { get; private set; }
}
There is mistake around the Local/office nav property. A few deliberate renames to clarify
Perhaps this....
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace one2one
{
class Program
{
static void Main(string[] args)
{
var context = new Demo();
var instructor = new Instructor();
instructor.FirstName = "Big";
instructor.LastName = "Willi";
context.Set<Instructor>().Add(instructor);
var office = new OfficeAssignment();
office.Location = "is this where the demo broke down ? See POCO ";
office.InstructorUsingThisOffice = instructor;
context.Set<OfficeAssignment>().Add(office);
context.SaveChanges();
}
}
public class OfficeAssignment
{
// Specifying InstructorID as a primary
public Int32 InstructorID { get; set; }
public string Location { get; set; }
// Navigation property
public virtual Instructor InstructorUsingThisOffice { get; set; }
}
public class Instructor
{
// Primary key
public int InstructorID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
//navigation
//missing
public virtual OfficeAssignment TheofficeToUse { get; set; }
}
public class Demo : DbContext
{
DbSet<OfficeAssignment> officeAssignments { get; set; }
DbSet<Instructor> Instructors { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure the primary key for the OfficeAssignment
modelBuilder.Entity<OfficeAssignment>()
.HasKey(t => t.InstructorID);
modelBuilder.Entity<Instructor>()
.HasRequired(t => t.TheofficeToUse)
.WithRequiredPrincipal(d => d.InstructorUsingThisOffice); //current entity is principal, the navigation back.
// and we share the same key... MUST with EF 1:1 foreign key
}
}
}
I have the following tables Essence, EssenseSet, and Essense2EssenceSet
Essense2EssenceSet is the linking table that creates the M:M relationship.
I've been unable to get the M:M relationship working though in EF code first though.
Here's my code:
[Table("Essence", Schema = "Com")]
public class Essence
{
public int EssenceID { get; set; }
public string Name { get; set; }
public int EssenceTypeID { get; set; }
public string DescLong { get; set; }
public string DescShort { get; set; }
public virtual ICollection<EssenceSet> EssenceSets { get; set; }
public virtual EssenceType EssenceType { get; set; }
}
[Table("EssenceSet", Schema = "Com")]
public class EssenceSet
{
public int EssenceSetID { get; set; }
public int EssenceMakerID { get; set; }
public string Name { get; set; }
public string DescLong { get; set; }
public string DescShort { get; set; }
public virtual ICollection<Essence> Essences { get; set; }
}
[Table("Essence2EssenceSet", Schema = "Com")]
public class Essence2EssenceSet
{
//(PK / FK)
[Key] [Column(Order = 0)] [ForeignKey("Essence")] public int EssenceID { get; set; }
[Key] [Column(Order = 1)] [ForeignKey("EssenceSet")] public int EssenceSetID { get; set; }
//Navigation
public virtual Essence Essence { get; set; }
public virtual EssenceSet EssenceSet { get; set; }
}
public class EssenceContext : DbContext
{
public DbSet<Essence> Essences { get; set; }
public DbSet<EssenceSet> EssenceSets { get; set; }
public DbSet<Essence2EssenceSet> Essence2EssenceSets { get; set; }
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<Essence>()
.HasMany(e => e.EssenceSets)
.WithMany(set => set.Essences)
.Map(mc =>
{
mc.ToTable("Essence2EssenceSet");
mc.MapLeftKey("EssenceID");
mc.MapRightKey("EssenceSetID");
});
}
}
This is the code I'm trying to run:
Essence e = new Essence();
e.EssenceTypeID = (int)(double)dr[1];
e.Name = dr[2].ToString();
e.DescLong = dr[3].ToString();
//Get Essence Set
int setID = (int)(double)dr[0];
var set = ctx.EssenceSets.Find(setID);
e.EssenceSets = new HashSet<EssenceSet>();
e.EssenceSets.Add(set);
ctx.Essences.Add(e);
ctx.SaveChanges();
And here's the error:
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception.
I'm not able to find the problem. I'd greatly appreciate help setting this up right.
Thanks!
Remove your Essence2EssenceSet model class. If junction table contains only keys of related entities participating in many-to-many relations it is not needed to map it as entity. Also make sure that your fluent mapping of many-to-many relations specifies schema for table:
mb.Entity<Essence>()
.HasMany(e => e.EssenceSets)
.WithMany(set => set.Essences)
.Map(mc =>
{
mc.ToTable("Essence2EssenceSet", "Com");
mc.MapLeftKey("EssenceID");
mc.MapRightKey("EssenceSetID");
});