我有三个表:Person
,Books
和PersonBook
。 Person
和Books
是主要表格,PersonBook
是它们之间的“映射表”。
逻辑非常简单:每个人都有多本书。在“添加/删除书籍”屏幕中,“人员”可以添加新书或删除现有书籍。
注意:为了便于理解,我仅描述了有限的专栏,但是 实际的表有大量的列和更多的约束。
人:
PersonId FirstName LastName
_____________________________________
1 Emma Watsan
2 John Peter
3 Albert Einsten
(....)
图书:
BookId BookName ISBN Author
___________________________________________________________
1 Beautiful Darkness
2 Bones Never Lies
3 The Lion
(...)
PersonBook:
Id PersonId BookId
___________________________
1 1 1
2 2 3
3 3 3
(...)
我有一个列表PersonBook
,我需要使用实体状态在表格中更新它。
我目前正在做以下事情:
Context.PersonBook.RemoveRange(Context.PersonBook.Where(m => m.PersonId == PersonId));
Context.PersonBook.AddRange(ListOfBooks);
这里我给出了一个简单的条件:
Context.PersonBook.Where(m => m.PersonId == PersonId)
但实际上,我有很多支票。
我的要求是仅删除本地集合ListOfBooks
中不存在的记录,并添加DbSet
中不存在的记录。
注意:每个人都有1K +书籍。在每个编辑请求中,比较 所有记录都会导致性能下降和我正在做的事情 目前比这个比较好,但它增加了 主键(自动增量键
PersonBook.Id
)值非常大。
答案 0 :(得分:4)
如果您准备并使用快速查找数据结构,那么比较会快得多。由于您正在处理个人图书,因此Dictionary<int, PersonBook>
作为关键字的旧记录和新记录可能是BookId
:
var oldBooks = Context.PersonBook.Where(e => e.PersonId == PersonId).ToDictionary(e => e.BookId);
var newBooks = ListOfBooks.ToDictionary(e => e.BookId);
Context.PersonBook.RemoveRange(oldBooks.Values.Where(e => !newBooks.ContainsKey(e.BookId)));
Context.PersonBook.AddRange(newBooks.Values.Where(e => !oldBooks.ContainsKey(e.BookId)));
答案 1 :(得分:0)
你几乎就在那里,使用contains来查看列表中是否有东西,如果是,则将其排除:
Context.PersonBook.RemoveRange(
Contect.PersonBook.where(m => m.PersonId == PersonId
&& !ListOfBooks.Contains(m)));
Context.PersonBook.AddRange(ListOfBooks.Where(r => !Context.PersonBook.Contains(r))));
答案 2 :(得分:0)
不是您的问题的答案,但您可以使用EF n-m关系功能 在您的情况下,配置应该是
modelBuilder.Entity<Person>()
.HasMany<Book>(user => user.OwnedBooks)
.WithMany(book => book.Owners)
.Map(mapping =>
{
mapping.MapLeftKey("PersonId");
mapping.MapRightKey("BookId");
mapping.ToTable("PersonBook");
});
使用此配置,EF将为您创建和处理联结表(您的PersonBook表)。如果启用自动迁移,您将看到EF创建的表包含2个字段和2个字段上的主键。
编辑
使用这些类
public class Person
{
public Person()
{
OwnedBooks = new List<Book>();
}
[Column("PersonId")]
public int Id { get; set; }
[MaxLength(50)]
public string FirstName { get; set; }
[MaxLength(50)]
public string LastName { get; set; }
public ICollection<Book> OwnedBooks { get; set; }
}
public class Book
{
public Book()
{
Owners = new List<Person>();
}
[Column("BookId")]
public int Id { get; set; }
[Column("BookName")]
[MaxLength(50)]
public string Name { get; set; }
[MaxLength(50)]
public string Isbn { get; set; }
[MaxLength(50)]
public string Author { get; set; }
public ICollection<Person> Owners { get; set; }
}
使用上面的配置(我认为你必须使用流畅的界面来配置m-n关系)你获得这个模型
ExecuteNonQuery==========
CREATE TABLE [Books] (
[BookId] int not null identity(1,1)
, [BookName] varchar(50) null
, [Isbn] varchar(50) null
, [Author] varchar(50) null
);
ALTER TABLE [Books] ADD CONSTRAINT [PK_Books_e63a83c6] PRIMARY KEY ([BookId])
ExecuteNonQuery==========
CREATE TABLE [People] (
[PersonId] int not null identity(1,1)
, [FirstName] varchar(50) null
, [LastName] varchar(50) null
);
ALTER TABLE [People] ADD CONSTRAINT [PK_People_e63a83c6] PRIMARY KEY ([PersonId])
ExecuteNonQuery==========
CREATE TABLE [PersonBook] (
[PersonId] int not null
, [BookId] int not null
);
ALTER TABLE [PersonBook] ADD CONSTRAINT [PK_PersonBook_e63a83c6] PRIMARY KEY ([PersonId], [BookId])
ExecuteNonQuery==========
CREATE INDEX [IX_PersonId] ON [PersonBook] ([PersonId])
ExecuteNonQuery==========
CREATE INDEX [IX_BookId] ON [PersonBook] ([BookId])
ExecuteNonQuery==========
ALTER TABLE [PersonBook] ADD CONSTRAINT [FK_PersonBook_People_PersonId] FOREIGN KEY ([PersonId]) REFERENCES [People] ([PersonId])
ExecuteNonQuery==========
ALTER TABLE [PersonBook] ADD CONSTRAINT [FK_PersonBook_Books_BookId] FOREIGN KEY ([BookId]) REFERENCES [Books] ([BookId])
然后我做了一些随机的插入/删除语句
Book book = new Book()
{
Author = "Niccolò Ammaniti",
Name = "Branchie"
};
book.Owners.Add(new Person() { FirstName = "Mastero" });
book.Owners.Add(new Person() { FirstName = "bubi" });
context.Books.Add(book);
context.SaveChanges();
这些是EF生成的查询
ExecuteDbDataReader==========
insert into [Books]([BookName], [Isbn], [Author])
values (@p0, null, @p1);
select [BookId]
from [Books]
where [BookId] = @@identity
@p0 = Branchie
@p1 = Niccolò Ammaniti
ExecuteDbDataReader==========
insert into [People]([FirstName], [LastName])
values (@p0, null);
select [PersonId]
from [People]
where [PersonId] = @@identity
@p0 = Mastero
ExecuteDbDataReader==========
insert into [People]([FirstName], [LastName])
values (@p0, null);
select [PersonId]
from [People]
where [PersonId] = @@identity
@p0 = bubi
ExecuteNonQuery==========
insert into [PersonBook]([PersonId], [BookId])
values (@p0, @p1);
@p0 = 1
@p1 = 1
ExecuteNonQuery==========
insert into [PersonBook]([PersonId], [BookId])
values (@p0, @p1);
@p0 = 2
@p1 = 1
然后更新/删除(来自Junction表,但对我们来说是透明的)
book = new Book()
{
Author = "Niccolò Ammaniti",
Name = "Come Dio comanda"
};
Person person = context.People.Single(_ => _.FirstName == "bubi");
person.OwnedBooks.Add(book);
context.SaveChanges();
book.Owners.Remove(person);
context.SaveChanges();
这些是查询
ExecuteDbDataReader==========
SELECT TOP 2
[Extent1].[PersonId] AS [PersonId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName]
FROM [People] AS [Extent1]
WHERE 'bubi' = [Extent1].[FirstName]
ExecuteDbDataReader==========
insert into [Books]([BookName], [Isbn], [Author])
values (@p0, null, @p1);
select [BookId]
from [Books]
where [BookId] = @@identity
@p0 = Come Dio comanda
@p1 = Niccolò Ammaniti
ExecuteNonQuery==========
insert into [PersonBook]([PersonId], [BookId])
values (@p0, @p1);
@p0 = 2
@p1 = 2
ExecuteNonQuery==========
delete from [PersonBook]
where (([PersonId] = @p0) and ([BookId] = @p1))
@p0 = 2
@p1 = 2
您可以从此处下载此代码(和其他有趣的配置)
它们是Microsoft Access(Jet)实体框架提供程序的测试,但很容易使它们适用于SQL Server。