我有两张桌子:
InboxEvent
int Id
Visitor Owner
[ForeignKey("Owner")]
string OwnerId
Visitor CausingUser
[ForeignKey("CausingUser")]
string CausingUserId
Visitor
string Id
OwnerId和CausingUserId都指向了Visitor表,但在Sql Server Entity Framework中已经进入并创建了InboxEvent表,如下所示:
FK OwnerId
FK CausingUserId
FK Visitor_Id
当然,它发明了一个专栏:
NVarChar(128) Visitor_Id
好的......很奇怪......很简单,我会通过手动迁移杀死流浪列:
public partial class KillInboxEventVisitor_Id : DbMigration
{
public override void Up()
{
DropForeignKey("dbo.InboxEvent", "Visitor_Id", "dbo.AspNetUsers");
DropIndex("dbo.InboxEvent", "IX_Visitor_Id");
DropColumn("dbo.InboxEvent", "Visitor_Id");
}
很好,它已经消失了!现在......每次我试图让EF保存一个InboxEvent:
db.InboxEvents.Add(new InboxEvent {...
await db.SaveChangesAsync();
EF爆炸
SqlException: Invalid column name 'Visitor_Id'.
如何判断基础EF模型这个想象的列是不存在的?
这是简单的Entity Framework 6.x,带有Asp.Net/OWIN Identity Framework,而不是较新的EF7 / Core。
我被要求展示全班。很高兴,但是,坚持你的屁股,他们在上面缩写是有原因的:
public class InboxEvent : IOwnerId
{
public int Id { get; set; }
[Index("IX_UserRead", 2)]
public DateTime CreatedOn { get; set; }
/// <summary>
/// The user whose Inbox this event is deposited into.
/// </summary>
[ForeignKey("Owner"), Required]
[MaxLength(128)]
[Index("IX_UserRead", 0), Index]
public string OwnerId { get; set; }
public Visitor Owner { get; set; }
[Index("IX_EventType_RelatedId1", 0)]
public InboxEventType EventType { get; set; }
public Posting.PostCore Post { get; set; }
[ForeignKey("Post")]
public int? PostId { get; set; }
public Posting.PostReply Reply { get; set; }
[ForeignKey("Reply")]
public int? ReplyId { get; set; }
[Index("IX_UserRead", 1)]
public bool IsRead { get; set; }
/// <summary>
/// The user, if any, that caused/triggered this event. For example if A upvotes B, this is A, and B is Visitor.
/// </summary>
[ForeignKey("CausingUser")]
[MaxLength(128)]
public string CausingUserId { get; set; }
public Visitor CausingUser { get; set; }
[Index]
[Index("IX_EventType_RelatedId1", 1)]
public int RelatedId1 { get; set; }
public int RelatedId2 { get; set; }
public InboxEvent()
{
CreatedOn = DateTime.UtcNow;
}
}
public class Visitor : IdentityUser<string, LoginIdentity, StandardUserRole, IdentityUserClaim>, IVisitor
{
[MaxLength(22), Column(TypeName = "Char"), Index]
public string Uid { get; set; }
[MaxLength(300), Index]
public string FirstName { get; set; }
[MaxLength(300), Index]
public string LastName { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime MemberOn { get; set; }
public int Invites { get; set; }
public int Score { get; set; }
public bool ShouldReceiveReplyNotifications { get; set; }
public DateTime LastEmailNotified { get; set; }
[MaxLength(100), Column(TypeName="VarChar")]
public string ProfilePic { get; set; }
public ICollection<UserPhoto> UserPhotos { get; set; }
public StaticImage StaticImage { get; set; }
// BioViewModel
[MaxLength(100)]
public string Nickname { get; set; }
[MaxLength(400)] // TODO Normalize
public string Disciplines { get; set; }
[MaxLength(1000)]
public string MissionStatement { get; set; }
[MaxLength(100)]
public string Tagline { get; set; }
// SkillsViewModel
[MaxLength(400)]
public string SkillsKnown { get; set; }
[MaxLength(400)]
public string SkillsToLearn { get; set; }
// PrefsViewModel
[MaxLength(10)]
public string PhoneCountryCode { get; set; }
//[MaxLength(20)]
//public string PhoneNumber { get; set; } // Already in base OWIN User model
public ChatService ChatService { get; set; }
[MaxLength(100)]
public string ChatHandle { get; set; }
public byte PrefIpOpenness { get; set; }
public byte PrefNonProfit { get; set; }
public byte PrefMature { get; set; }
public PrivacyLevel PrivacyNameCountryCity { get; set; }
public PrivacyLevel PrivacyBio { get; set; }
public PrivacyLevel PrivacySites { get; set; }
public PrivacyLevel PrivacyCompanies { get; set; }
public PrivacyLevel PrivacySkills { get; set; }
public PrivacyLevel PrivacyContactInfo { get; set; }
public PrivacyLevel PrivacyCollabPrefs { get; set; }
public bool WantMonthlyNewsletter { get; set; }
public bool WantToMentor { get; set; }
public bool WantToVetProjects { get; set; }
public bool WantToReviewProjects { get; set; }
public bool WantToGiveFeedback { get; set; }
// CommitmentViewModel
public CommitmentChoice CommitmentChoice { get; set; }
[MaxLength(1000)]
public string CommitmentText { get; set; }
public ICollection<CountryZip> CountryZips { get; set; }
public ICollection<Org> Orgs { get; set; }
public ICollection<UserSite> UserSites { get; set; }
public ICollection<DraftData> Drafts { get; set; }
public ICollection<Visitor_Team> Teams { get; set; }
public ICollection<InboxEvent> Inbox { get; set; }
public ICollection<SubscribedPost> PostSubscriptions { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<Visitor, string> 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 Visitor()
{
CreatedOn = (DateTime)SqlDateTime.MinValue;
MemberOn = (DateTime)SqlDateTime.MinValue;
LastEmailNotified = (DateTime)SqlDateTime.MinValue;
}
}
Db确实有一个OnModelsCreating虽然它不太可能与此相关:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
var entity = modelBuilder.Entity<Visitor>();
// By default this is nvarchar max? Uh?
entity.Property(v => v.PhoneNumber).HasMaxLength(20);
}
答案 0 :(得分:2)
Visitor
和InboxEvent
之间有三种关联。你最初展示的两个:
public class InboxEvent : IOwnerId
{
...
[ForeignKey("Owner"), Required]
public string OwnerId { get; set; }
public Visitor Owner { get; set; }
[ForeignKey("CausingUser")]
public string CausingUserId { get; set; }
public Visitor CausingUser { get; set; }
...
}
和
public class Visitor : IdentityUser<string, ...
{
...
public ICollection<InboxEvent> Inbox { get; set; }
...
}
如果没有进一步通知,EF将假设这些关联彼此独立:它将创建三个FK,其中Visitor_Id
用于后一个关联。
您可能希望InboxEvent.Owner
和Visitor.Inbox
成为一个关联的两端,但EF并不知道这一点,也不会猜测。您必须明确说明,例如使用[InverseProperty]
属性:
public class InboxEvent : IOwnerId
{
...
[ForeignKey("Owner"), Required]
public string OwnerId { get; set; }
[InverseProperty("Inbox")]
public Visitor Owner { get; set; }
...
}