我在这里有另一个实体框架问题。我有一个名为Book的复杂对象,该对象有许多类型为Contributor的集合,例如Writer,Letterer,Colorist等。但贡献者不一定是特定角色的范围。因此,相同的贡献者(具有相同的ContributorId)可以是Writer和Colorist。
public Book {
public ICollection<Contributor> Writers { get; set; }
public ICollection<Contributor> Artists { get; set; }
public ICollection<Contributor> Pencilers { get; set; }
public ICollection<Contributor> Inkers { get; set; }
public ICollection<Contributor> Colorists { get; set; }
public ICollection<Contributor> Letterers { get; set; }
public ICollection<Contributor> CoverArtists { get; set; }
public ICollection<Contributor> OtherContributors { get; set; }
}
public Contributor {
public int ContributorId { get; set; }
public string Name { get; set; }
}
我遇到了麻烦,查看我在此处和其他网站上找到的示例,确定如何表示相应的模型。我希望Db Model能像这样。我想要避免的是一个模型,其中我为每个贡献者角色都有一个单独的表,或者对于每个贡献者与任何角色的书相关联的实例,在Contributor表中有一个单独的行。
+ Books
--BookId
+ Contributors
--ContributorId
+ BookContributors
--BookId
--ContributorId
--Discriminator
我就像ADO.NET那样,我并不是真的觉得这太令人愉快了,但我决心至少在这个重要的框架中熟练掌握。
快速注释: 自从打开这个问题以来,我在工作中被拉开了,没有时间彻底审查答案并玩弄结果。但我不想放弃赏金,因为我很欣赏每个人提供的答案。所以我选择了对我来说最感兴趣的答案。我想为此感谢大家。
答案 0 :(得分:3)
我已经开发了一个实现您提出的模型的解决方案,尽管它的工作方式与您预期的有所不同。希望这能回答你的问题。
<强>模型强>
[Table("Book")]
public class Book
{
[Column("BookId")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int BookId { get; set; }
[NotMapped]
public ICollection<Contributor> Writers { get; set; }
[NotMapped]
public ICollection<Contributor> Artists { get; set; }
[NotMapped]
public ICollection<Contributor> Pencilers { get; set; }
[NotMapped]
public ICollection<Contributor> Inkers { get; set; }
[NotMapped]
public ICollection<Contributor> Colorists { get; set; }
[NotMapped]
public ICollection<Contributor> Letterers { get; set; }
[NotMapped]
public ICollection<Contributor> CoverArtists { get; set; }
[NotMapped]
public ICollection<Contributor> OtherContributors { get; set; }
}
[Table("Contributor")]
public class Contributor
{
[Column("ContributorId")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContributorId { get; set; }
}
// Contributor Type is one of the following options: Writer, Artist, Penciler, etc.
[Table("ContributorType")]
public class ContributorType
{
[Column("ContributorTypeId")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContributorTypeId { get; set; }
[Column("Name")]
public string Name { get; set; }
}
[Table("BookContributor")]
public class BookContributor
{
[Column("BookContributorId")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int BookContributorId { get; set; }
[Column("BookId")]
public int BookId { get; set; }
[Column("ContributorId")]
public int ContributorId { get; set; }
[Column("RoleId")]
public int RoleId { get; set; }
[ForeignKey("BookId")]
public virtual Book Book { get; set; }
[ForeignKey("ContributorId")]
public virtual Contributor Contributor { get; set; }
[ForeignKey("RoleId")]
public virtual ContributorType Role { get; set; }
}
数据库上下文
AppDbContext.cs:
public class AppDbContext : DbContext
{
public AppDbContext()
{
Database.SetInitializer<AppDbContext>(new AppDbInitializer());
}
public AppDbContext(string connectionString)
: base(connectionString)
{
Database.SetInitializer<AppDbContext>(new AppDbInitializer());
}
public DbSet<Book> Books { get; set; }
public DbSet<Contributor> Contributors { get; set; }
public DbSet<ContributorType> ContributorTypes { get; set; }
public DbSet<BookContributor> BookContributors { get; set; }
}
AppDbInitializer.cs:
public class AppDbInitializer : DropCreateDatabaseAlways<AppDbContext>
{
protected override void Seed(AppDbContext context)
{
// default contributor types
var contributorTypes = new List<ContributorType>();
contributorTypes.Add(new ContributorType() { Name = "Writer" });
contributorTypes.Add(new ContributorType() { Name = "Artist" });
contributorTypes.Add(new ContributorType() { Name = "Penciler" });
contributorTypes.Add(new ContributorType() { Name = "Inker" });
contributorTypes.Add(new ContributorType() { Name = "Colorist" });
contributorTypes.Add(new ContributorType() { Name = "Letterer" });
contributorTypes.Add(new ContributorType() { Name = "CoverArtist" });
contributorTypes.Add(new ContributorType() { Name = "OtherContributor" });
// adding it to the context
foreach (var type in contributorTypes)
context.ContributorTypes.Add(type);
base.Seed(context);
}
}
将所有内容整合在一起
的Program.cs:
class Program
{
static void Main(string[] args)
{
// enter name of the connection string in App.Config file
var connectionSettings = ConfigurationManager.ConnectionStrings["..."];
using (var dbContext = new AppDbContext(connectionSettings.ConnectionString))
{
// Creating a book
var book = new Book();
dbContext.Books.Add(book);
dbContext.SaveChanges();
// Creating contributor
var contributor = new Contributor();
dbContext.Contributors.Add(contributor);
dbContext.SaveChanges();
// Adding contributor to the book
var bookContributor = new BookContributor()
{
BookId = book.BookId,
ContributorId = contributor.ContributorId,
RoleId = dbContext.ContributorTypes.First(t => t.Name == "Writer").ContributorTypeId
};
dbContext.BookContributors.Add(bookContributor);
dbContext.SaveChanges();
// retrieving a book
var book = dbContext.Books.Where(b => b.BookId == 2).FirstOrDefault();
if (book != null)
{
book.Writers =
from contributor in dbContext.Contributors
join bookContributor in dbContext.BookContributors on contributor.BookId equals bookContributor.BookId
join contributorType in dbContext.ContributorTypes on contributorType.ContributorTypeId equals bookContributor.ContributorTypeId
where
bookContributor.BookId == 2 and
contributorType.Name == "Writer"
select contributor;
// do the same for other types of contributors
}
}
}
}
答案 1 :(得分:3)
使用M:N映射在Contributor实体中创建类似的集合,并使用InverseProperty属性声明Contributor类中的哪个集合与Book类中的哪个集合相对应。
public class Book
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Contributor> Writers { get; set; }
public virtual ICollection<Contributor> Artists { get; set; }
public virtual ICollection<Contributor> Pencilers { get; set; }
public virtual ICollection<Contributor> Inkers { get; set; }
public virtual ICollection<Contributor> Colorists { get; set; }
public virtual ICollection<Contributor> Letterers { get; set; }
public virtual ICollection<Contributor> CoverArtists { get; set; }
public virtual ICollection<Contributor> OtherContributors { get; set; }
public Book()
{
Writers = new List<Contributor>();
Artists = new List<Contributor>();
Pencilers = new List<Contributor>();
Inkers = new List<Contributor>();
Colorists = new List<Contributor>();
Letterers = new List<Contributor>();
CoverArtists = new List<Contributor>();
OtherContributors = new List<Contributor>();
}
}
public class Contributor
{
public int ContributorId { get; set; }
public string Name { get; set; }
[InverseProperty("Writers")]
public virtual ICollection<Book> WriterOfBooks { get; set; }
[InverseProperty("Artists")]
public virtual ICollection<Book> ArtistOfBooks { get; set; }
[InverseProperty("Pencilers")]
public virtual ICollection<Book> PencilerOfBooks { get; set; }
[InverseProperty("Inkers")]
public virtual ICollection<Book> InkerOfBooks { get; set; }
[InverseProperty("Colorists")]
public virtual ICollection<Book> ColoristOfBooks { get; set; }
[InverseProperty("Letterers")]
public virtual ICollection<Book> LettererOfBooks { get; set; }
[InverseProperty("CoverArtists")]
public virtual ICollection<Book> CoverArtistOfBooks { get; set; }
[InverseProperty("OtherContributors")]
public virtual ICollection<Book> OtherContributorOfBooks { get; set; }
public Contributor()
{
WriterOfBooks = new List<Book>();
ArtistOfBooks = new List<Book>();
PencilerOfBooks = new List<Book>();
InkerOfBooks = new List<Book>();
ColoristOfBooks = new List<Book>();
LettererOfBooks = new List<Book>();
CoverArtistOfBooks = new List<Book>();
OtherContributorOfBooks = new List<Book>();
}
}
使用非常简单:
using (var dc = new MyDbContext())
{
// create sample data
var book1 = new Book() { Name = "Book 1" };
dc.Books.Add(book1);
var contrib1 = new Contributor() { Name = "Contributor 1" };
var contrib2 = new Contributor() { Name = "Contributor 2" };
var contrib3 = new Contributor() { Name = "Contributor 3" };
dc.Contributors.Add(contrib1);
dc.Contributors.Add(contrib2);
dc.Contributors.Add(contrib3);
dc.SaveChanges();
// add relationships
book1.Writers.Add(contrib1);
book1.Artists.Add(contrib1);
book1.Artists.Add(contrib2);
book1.OtherContributors.Add(contrib3);
dc.SaveChanges();
}
// verify that the contributor 1 has both Artist and Writer relations
using (var dc = new MyDbContext())
{
var contrib1 = dc.Contributors.Single(c => c.Name == "Contributor 1");
var hasWriter = contrib1.WriterOfBooks.Count == 1;
var hasArtist = contrib1.ArtistOfBooks.Count == 1;
if (!hasWriter || !hasArtist)
{
throw new Exception("Houston, we have a problem.");
}
}
答案 2 :(得分:2)
我正在test.polarcomputer.com上读书
如果你有一个书籍对象,这个对象有作家,出版商,设计师......那么,你只需要3个对象:
1.book对象
2.贡献者对象。
3.整合对象
书籍对象有
- bookid
- 书名
贡献者对象有
- 贡献者
- 姓名
- typeofcontributor // 0-writer 1-colorist 2-CoverArtists 3-whoever
集成对象有
- bookid
- 贡献者
- typeofcontributor // 0-writer 1-colorist 2-CoverArtists 3-whoever
检查这一点,如果我真的理解它,我可以给你完整的解决方案。
答案 3 :(得分:2)
您显示的数据模型没问题,但有一件事是清楚的。您无法将其映射为纯粹的多对多关联。只有当联结表BookContributors
仅包含BookId
和ContributorId
时,才有可能。
所以你总是需要一个明确的BookContributor
类,并且获得一个贡献者类型的集合总是会采用这种基本形状:
book.BookContributors
.Where(bc => bc.Type == type)
.Select(bc => bc.Contributor)
与你的想法相比,笨拙。但是我害怕,没办法解决它。剩下的是实现细节中的一些选项。
首先,让我们得到正确的基本模型:
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public virtual ICollection<BookContributor> BookContributors { get; set; }
}
public class Contributor
{
public int ContributorId { get; set; }
public string Name { get; set; }
public virtual ICollection<BookContributor> BookContributors { get; set; }
}
public class BookContributor
{
public int BookId { get; set; }
public virtual Book Book { get; set; }
public int ContributorId { get; set; }
public virtual Contributor Contributor { get; set; }
public string Type { get; set; }
}
映射:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Book>().HasMany(b => b.BookContributors)
.WithRequired(bc => bc.Book)
.HasForeignKey(bc => bc.BookId);
modelBuilder.Entity<Contributor>().HasMany(c => c.BookContributors)
.WithRequired(bc => bc.Contributor)
.HasForeignKey(bc => bc.ContributorId);
modelBuilder.Entity<BookContributor>()
.HasKey(bc => new {bc.BookId, bc.ContributorId, bc.Type});
}
(顺便说一下,在这里我避免使用“判别器”一词,因为这表明TPH继承,但这不适用)。
现在您可以向Book
添加属性,如下所示:
[NotMapped]
public IEnumerable<Contributor> Writers
{
get
{
return BookContributors.Where(bc => bc.Type == "writer")
.Select(bc => bc.Contributor);
}
}
这种方法的缺点是,您始终必须确保书籍加载了BookContributors
和他们的 Contributor
,或者延迟加载是可能的。并且您不能直接在LINQ查询中使用这些属性。此外,获取书籍并且只有他们独特的贡献者(即不同的)有点困难。
您可以使BookContributor
成为具有多个继承者的抽象基类:
public abstract class BookContributor
{
public int Id { get; set; }
public int BookId { get; set; }
public virtual Book Book { get; set; }
public int ContributorId { get; set; }
public virtual Contributor Contributor { get; set; }
}
public class Artist : BookContributor
{ }
public class Writer : BookContributor
{ }
BookContributor
现在需要一个代理键Id
,因为EF现在将使用隐藏的字段Discriminator
,因此无法将其配置为主键的一部分
现在Book
可以拥有类似......
[NotMapped]
public ICollection<Artist> Artists
{
get { return BookContributors.OfType<Artist>().ToList(); }
}
......但是这些仍然会有与上面提到的相同的缺点。唯一可能的优点是您现在可以使用类型(使用编译时检查)而不是字符串(或枚举值)来获取各种BookContributor
类型。
也许最有希望的方法是略有不同的模式:书籍和贡献者,他们之间的每个关联都可以有一组贡献者类型。 BookContributor
现在看起来像这样:
public class BookContributor
{
public int BookId { get; set; }
public virtual Book Book { get; set; }
public int ContributorId { get; set; }
public virtual Contributor Contributor { get; set; }
public virtual ICollection<BookContributorType> BookContributorTypes { get; set; }
}
一种新型:
public class BookContributorType
{
public int ID { get; set; }
public int BookId { get; set; }
public int ContributorId { get; set; }
public string Type { get; set; }
}
修改后的映射:
modelBuilder.Entity<BookContributor>().HasKey(bc => new { bc.BookId, bc.ContributorId });
附加映射:
modelBuilder.Entity<BookContributor>().HasMany(bc => bc.BookContributorTypes).WithRequired();
modelBuilder.Entity<BookContributorType>().HasKey(bct => bct.ID);
使用此模型,如果您对贡献者的类型不感兴趣,您可以获得书籍及其独特的贡献者......
context.Books.Include(b => b.BookContributors
.Select(bc => bc.Contributor))
......或者类型......
context.Books.Include(b => b.BookContributors
.Select(bc => bc.Contributor))
.Include(b => b.BookContributors
.Select(bc => bc.BookContributorTypes));
......或只有作家的书......
context.Books.Select(b => new
{
Book = b,
Writers = b.BookContributors
.Where(bc => bc.BookContributorTypes
.Any(bct => bct.Type == "artist"))
})
同样,后一个查询可以包含在属性中......
[NotMapped]
public ICollection<Artist> Artists
{
get
{
return BookContributors
.Where(bc => bc.BookContributorTypes
.Any(bct => bct.Type == "artist"))
.Select(bc => bc.Contributor).ToList();
}
}
......但是,所有上述注意事项。
答案 4 :(得分:0)
你的模特应该是这样的:
[Table("tblBooks")]
public class BookTbl
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int BookID { get; set; }
public string BookName { get; set; }
}
[Table("tblContributor")]
public class ContributorTbl
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ContID { get; set; }
public string Contributor { get; set; }
}
[Table("tblIntegration")]
public class IntegrationTbl
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int IntID { get; set; }
public int BookID { get; set; }
[ForeignKey("BookID")]
public BookTbl Book { get; set; }
public int ContID { get; set; }
[ForeignKey("ContID")]
public IntegrationTbl Integration { get; set; }
}