如何基于不同对象类型的另一个列表从数据库中获取列表?

时间:2018-07-04 09:56:02

标签: c# linq entity-framework-core

我有以下两种模型:

public class Product
{
    public int Id { get; set; }
    public int ProductGroupId { get; set; }
    public int ProductGroupSortOrder { get; set; }
    // ... some more properties
    public ICollection<ProductInCategory> InCategories { get; set; }
}

public class ProductInCategory
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public int ProductCategoryId { get; set; }
    public int SortOrder { get; set; }

    // Nav.props.:
    public Product Product { get; set; }
    public ProductCategory ProductCategory { get; set; }
}

有些Product通过属性ProductGroupId分组在一起,我希望能够从Product中一次删除整组ProductInCategory数据库查询。

控制器方法接收product_idcategory_id,而不是ProductGroupId

对于单个Product,我一直在使用此查询将其从类别中删除:

ProductInCategory unCategorize = await _context.ProductsInCategories
    .Where(pic => pic.ProductId == product_id && pic.ProductCategoryId == category_id)
    .FirstOrDefaultAsync();

然后:

_context.Remove(unCategorize);
await _context.SaveChangesAsync();

现在,如果我有一个要从List<Product>中删除的ProductsInCategories,查询将是什么样?

我已经尝试过了,但是在.Any()位上却失败了:

Product product = await _context.Products
    .Where(p => p.Id == product_id)
    .FirstOrDefaultAsync();

List<Product> products = await _context.Products
    .Where(g => g.ProductGroupId == product.ProductGroupId)
    .ToListAsync();

List<ProductInCategory> unCategorize = await _context.ProductsInCategories
    .Where(pic => pic.ProductId == products.Any(p => p.Id)
        && pic.ProductCategoryId == category_id)
    .ToListAsync();

4 个答案:

答案 0 :(得分:2)

  

控制器方法接收product_idcategory_id,而不是ProductGroupId

第一个问题是,为什么该方法需要product_id来接收ProductGroupId

这闻起来设计不好,但是无论如何,首先将product_id转换为所需的ProductGroupId(这将使我们花费额外的数据库查询):

int? productGroupId = await _context.Products
    .Where(p => p.Id == product_id)
    .Select(p => (int?)p.ProductGroupId)
    .FirstOrDefaultAsync();

if (productGroupId == null)
{
    // Handle non existing product_id 
}

剩下的事情很简单,只需访问LINQ to Entities查询中的导航属性,该属性将由EF Core转换为生成的SQL查询中的相应联接。不需要中间的Product列表。

List<ProductInCategory> unCategorize = await _context.ProductsInCategories
    .Where(pic => pic.Product.ProductGroupId == productGroupId)
    .ToListAsync();

答案 1 :(得分:1)

尝试更改为以下内容:

Mail::to("xyz.gmail.com")->send(new contactMailAdmin($userData));

答案 2 :(得分:1)

我可能会针对您想要的内容提出代码修复建议,但是有一个更好的解决方案:n 从加载数据开始

在您的代码示例中,您要从数据库加载数据,然后告诉EF删除加载的项目。那不是很有效。应该没有理由加载数据,您应该能够简单地执行查询而无需加载数据。

据我所知,Entity Framework无法“条件删除”(因为缺少更好的名称),例如:

DELETE FROM People WHERE Name = 'Bob' 

如果要基于特定的列值(实体的ID除外)删除项目,则除非依赖于加载数据(这会降低性能),否则不能依赖Entity Framework。

这里有两个更好的选择:

1。自己执行SQL查询

context.Database.ExecuteSqlCommand(
        "DELETE FROM Products WHERE ProductGroupId = " + product.ProductGroupId 
      ); 

这就是我一直这样做的方式。

  

旁注:我期待有关SQL注入的评论。需要明确的是:这里没有SQL注入的危险,因为product.ProductGroupId不是字符串,并且其值是由开发人员而不是最终用户控制的。
  但是,我确实同意使用SQL参数是一种好习惯。但是在这个答案中,我想提供一个简单的示例来展示如何执行包含SQL的字符串。

2。找到一个可以删除而不加载的库。

我只是在谷歌搜索时才偶然发现这一点。 Entity Framework Extensions似乎已经实现了条件删除功能:

context.Customers.Where(x => x.ID == userId).DeleteFromQuery();

在您的情况下,应该是:

_context.Products.Where(g => g.ProductGroupId == product.ProductGroupId).DeleteFromQuery();

边注
我一直使用代码优先,并且EF总是自动为我生成级联删除。因此,删除父级时,其子级也会被删除。我不确定您的数据库是否具有级联的删除操作,但是我假设是默认的EF行为(根据我的经验)。

答案 3 :(得分:0)

在先前的LINQ查询中,products值可能设置为null。添加另一个条件来验证这一点。

.Where(pic => products && pic.ProductId == products.Any(p => p.Id)
    && pic.ProductCategoryId == secondary_id)