我有两个表,我想在 EF 中创建从
User
到 Contact
的 1-n 关系并反转。这意味着我需要与 2 个目的地建立 2 种关系,它们也是基表。
用户类中的Contact
表应该引入为ICollection
,所以我的关系出了问题,它们都是一侧。但是如果我删除ICollection
,一切都是正确的。
我该如何解决这个问题?
public class User
{
[Key]
public Guid Id {get; set;}
[ForeignKey("ContactId")]
public virtual ICollection<Contact> Contacts {get; set;}
public Guid? ContactId {get;set;}
}
public class Contact
{
[Key]
public Guid Id {get; set;}
[ForeignKey("UserId")]
public virtual User User {get; set;}
public Guid? UserId {get; set;}
}
有很多方法可以实现这一目标。您可以按照以下方式进行操作
public class User
{
[Key]
public Guid Id {get; set;}
public virtual ICollection<Contact> Contacts { get; set; }
}
public class Contact
{
[Key]
public Guid Id {get; set;}
public virtual User User {get; set;}
}
或
你也可以尝试这个方法
public class User
{
[Key]
public Guid UserId {get; set;}
public virtual ICollection<Contact> Contacts { get; set; }
}
public class Contact
{
[Key]
public Guid Id {get; set;}
public Guid UserId {get; set;}
public virtual User User {get; set;}
}
它包括外键属性
UserId
欲了解更多信息,您可以前往此链接
public class User
{
[Key]
public Guid Id {get; set;}
// [ForeignKey("ContactId")]
public virtual ICollection<Contact> Contacts {get; set;}
// public Guid? ContactId {get;set;}
}
1-n 关系的 1 边没有外键。
如果您需要从用户到联系人的 1-n 关系,您需要额外的导航属性。要确定 EF 行为,您还可以配置模型。 这是一个例子。
[Table("User78")]
public class User
{
[Key]
public int Id { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
public virtual User MyPreferredUser { get; set; }
}
[Table("Contact78")]
public class Contact
{
[Key]
public int Id { get; set; }
public virtual User User { get; set; }
}
public class Context : DbContext
{
public Context()
{ }
这是上下文配置
public Context(DbConnection connection)
: base(connection, true)
{ }
public DbSet<User> Users { get; set; }
public DbSet<Contact> Contacts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany(_ => _.Contacts)
.WithOptional(_ => _.User)
.Map(_ => _.MapKey("UserId"));
modelBuilder.Entity<User>()
.HasOptional(_ => _.MyPreferredUser)
.WithMany()
.Map(_ => _.MapKey("ContactId"));
}
}
在此示例中,您无法使用 MyPreferredUser 关系从联系人导航到用户。如果需要,您需要添加从联系人到用户(ICollection 类型)的新导航。
这是迁移时EF生成的DML
ExecuteNonQuery==========
CREATE TABLE [Contact78] (
[Id] int not null identity(1,1)
, [UserId] int null
);
ALTER TABLE [Contact78] ADD CONSTRAINT [PK_Contact78_a31c6496] PRIMARY KEY ([Id])
ExecuteNonQuery==========
CREATE TABLE [User78] (
[Id] int not null identity(1,1)
, [ContactId] int null
);
ALTER TABLE [User78] ADD CONSTRAINT [PK_User78_a31c6496] PRIMARY KEY ([Id])
ExecuteNonQuery==========
CREATE INDEX [IX_UserId] ON [Contact78] ([UserId])
ExecuteNonQuery==========
CREATE INDEX [IX_ContactId] ON [User78] ([ContactId])
ExecuteNonQuery==========
ALTER TABLE [Contact78] ADD CONSTRAINT [FK_Contact78_User78_UserId] FOREIGN KEY ([UserId]) REFERENCES [User78] ([Id])
ExecuteNonQuery==========
ALTER TABLE [User78] ADD CONSTRAINT [FK_User78_User78_ContactId] FOREIGN KEY ([ContactId]) REFERENCES [User78] ([Id])
在这里查看我正在添加 BlogPost 和类别表之间的关系
public class BlogPost
{
public Guid Id { get; set; }
public string Title { get; set; }
public string ShortDescription { get; set; }
public string Content { get; set; }
public string FeaturedImageUrl { get; set; }
public string UrlHandle { get; set; }
public DateTime PublishedDate { get; set; }
public string Author { get; set; }
public bool IsVisible { get; set; }
public ICollection<Category> Categories { get; set; }
}
public class Category
{
public Guid Id { get; set; }
public string Name { get; set; }
public string UrlHandle { get; set; }
public ICollection<BlogPost> BlogPosts { get; set; }
}
控制器动作方法
[HttpPost]
public async Task<IActionResult> CreateBlogPost([FromBody] CreateBlogPostRequestDTO createBlogPostRequestDTO)
{
var blogPost = new BlogPost
{
Title = createBlogPostRequestDTO.Title,
Author = createBlogPostRequestDTO.Author,
Content = createBlogPostRequestDTO.Content,
FeaturedImageUrl = createBlogPostRequestDTO.FeaturedImageUrl,
IsVisible = createBlogPostRequestDTO.IsVisible,
PublishedDate = createBlogPostRequestDTO.PublishedDate,
ShortDescription = createBlogPostRequestDTO.ShortDescription,
UrlHandle = createBlogPostRequestDTO.UrlHandle,
Categories = new List<Category>()
};
foreach(var item in createBlogPostRequestDTO.Categories)
{
var existingCategory = await iCategoryRepo.GetById(item);
if(existingCategory is not null)
{
blogPost.Categories.Add(existingCategory);
}
}
blogPost = await iBlogPostRepo.CreateAsync(blogPost);
var blogPostDTO = new
{
Id = blogPost.Id,
Title = blogPost.Title,
Author = blogPost.Author,
Content = blogPost.Content,
FeaturedImageUrl = blogPost.FeaturedImageUrl,
IsVisible = blogPost.IsVisible,
PublishedDate = blogPost.PublishedDate,
ShortDescription = blogPost.ShortDescription,
UrlHandle = blogPost.UrlHandle,
CategoryDtos = blogPost.Categories.Select(x=> new CategoryDto
{
Id = x.Id,
Name = x.Name,
UrlHandle = x.UrlHandle
}).ToList()
};
return Ok(blogPostDTO);
}