无需手动比较即可添加或删除多个记录

时间:2017-07-05 11:32:40

标签: c# entity-framework entity-framework-6

我有三个表:PersonBooksPersonBookPersonBooks是主要表格,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)值非常大。

3 个答案:

答案 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

您可以从此处下载此代码(和其他有趣的配置)

https://github.com/bubibubi/JetEntityFrameworkProvider/tree/master/JetEntityFrameworkProvider.Test/Model60_StackOverflow_m2n_Person_Book

它们是Microsoft Access(Jet)实体框架提供程序的测试,但很容易使它们适用于SQL Server。