如何根据 DBSet<> 中的 ID 找到实体?

问题描述

我有一个包含地址的数据库地址,每个地址都有一个唯一的 ID。 我需要编写一个方法,将获取和 ID 作为参数,它必须检查数据库地址中是否存在具有给定 ID 的地址。

我尝试了以下方法

var listofRightIDs = dbContext.Addresses.Where(a => a.Id != givenAddress).ToList();
            if (listofRightIDs.Count == 0)
                throw new ArgumentException();

解决方法

int addressId = 234 //put your address id in here    

var address = dbContext.Addresses.Where(a => a.Id == addressId).SingleOrDefault();
    
                if (address == null)
                    throw new ArgumentException();

注意:您只获取一个地址,因为您说 Id 是唯一的,因此请改用 SingleOrDefault。

,

有两种方法可以从 DbSet<T> 中获取具有特定主键的项目:

  • 使用查询,以 FirstOrDefault() 结尾
  • 使用DbSet.Find

DbSet.Find:如果上下文中存在具有给定主键值的实体,则它会立即返回,而无需向商店发出请求。否则,将向商店请求具有给定主键值的实体和该实体

这两个你应该用哪一个取决于用法:如果你取了项目,然后做一些计算,而别人改变了数据库中的项目,你想再次使用该项目,你想要原来取的项目吗?项目,还是您想要更新的项目?

另外:你获取数据是因为你想更新数据库中获取的数据,还是因为你只想使用获取的数据?

您的 DbContext 有一个 ChangeTracker。每当您在不使用 Select 的情况下查找或查询数据时,获取的数据及其副本都会放入 ChangeTracker。你得到了对副本的引用(或者可能是原始的,但因为它们是相同的,所以没关系)

如果您对获取的数据进行更改,则会更改副本。当您调用 SaveChanges 时,会将原始值与副本的值进行比较。如果它们发生更改,则该项目会在数据库中更新。

如果您使用 Find 获取完整的项目,然后再次使用 Find 获取相同的项目,则会从 ChangeTracker 获取值。如果您使用查询,该项目将再次从数据库中获取:

假设客户 10 从“哈勒姆”移动到“阿姆斯特丹”:

int customerId = 10;

// fetch the customer with this Id,you can either use Find or FirstOrDefault:
Customer customerX = dbSet.Customers.Find(customerId);
Customer customerX = dbSet.Customers.Where(customer => customer.Id == customerId)
                                    .FirstOrDefault();

现在更改客户:

customerX.City = "Amsterdam";

再次获取客户: 客户 customerY = dbSet.Customers.Find(customerId);

此客户已在 ChangeTracker 中,您可以从 ChangeTracker 中获取更改后的值。 customerY 住在“阿姆斯特丹”

Customer customerZ = dbSet.Customers.Where(customer => customer.Id == customerId)
                                    .FirstOrDefault();

customerZ 从数据库中查询。您尚未保存更改,因此该客户仍住在“哈勒姆”。如果其他人已将此客户移至“鹿特丹”,您将从“鹿特丹”获得该客户。

所以当再次通过主键请求同一个对象时,这取决于你想要什么,你是要使用Find还是Where(... => ... Id == id).FirstOrDefault()

顺便说一句,我不知道这是否会覆盖最初获取的客户。您可以通过检查是否 Object.ReferenceEquals(CustomerX,CustomerZ) 或检查 CustomerX 是否住在“哈勒姆”或“阿姆斯特丹”来检查这一点。我的猜测是这个客户被新获取的数据覆盖了。

因此,如果您再次获取相同的物品,请注意选择哪一个。

还有改进的空间。

通常 dbContext 的生命周期很短:

using (var dbContext = new CustomerDbContext())
{
     // Fetch an item,change it and SaveChanges
}

但有时你会拿到很多东西。如果您在不使用 Select 的情况下获取它们,则所有获取的项目都将在 ChangeTracker 中:

using (var dbContext = new MyDbContext())
{
    var newYorkCustomers = dbContext.Customers
        .Where(customer => customer.City == "New York")
        .ToList();
    this.Display(newYorkCustomers);

这将使成千上万的客户进入 ChangeTracker。现在假设您只更改第一个 Customer 并调用 SaveChanges:

    newYorkCustomer[0].Obsolete = true;
    dbContext.SaveChanges();

代码必须检查 ChangeTracker 中的所有 100,000 个客户,以查看更改了哪一个。

无论何时查询数据,始终使用 Select 来获取数据,并且仅选择您计划更改的属性。如果您计划更新已提取的项目,则仅提取而不选择。

Include 也是如此。如果您获取“客户及其订单”,则仅在您计划更新获取的订单时使用“包含”,否则,请使用“选择”