删除实体而不在通用存储库模式实体框架中获取它

Ran*_*ana 3 c# sql generics entity-framework repository-pattern

我正在尝试使用通用存储库模式从数据库中删除 Employee 实体,该数据库包含不同的表,例如 Employee、Project、Skills。

namespace Information.Repository
{
    public class IRepositoy<TEntity> : IRepository<TEntity> where TEntity : class
    {
        private readonly ApplicationDbContext _dbContext;

        public IRepositoy(ApplicationDbContext dbContext)
        {
            _dbContext = dbContext;
        }
        public void Remove(int id)
        {
            TEntity element = _dbContext.Set<TEntity>().Find(id);
            _dbContext.Set<TEntity>().Remove(element);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当上面的Remove方法被调用时,它会进行两次数据库调用

  1. 一种用于获取实体。

  2. 第二个用于删除它。

我发现像下面这样的查询,当实体类型(员工或项目或技能)已知时,该查询使用单个 SQL 查询执行

 public void Remove(int id)
        {
            Employee employee = new Employee { EmployeeId = id };
            _dbContext.Entry(employee).State = EntityState.Deleted;
        }
Run Code Online (Sandbox Code Playgroud)

任何人都可以建议我如何删除实体而不使用类似于上面示例的通用存储库模式来获取它。

Fla*_*ter 5

使用原始 SQL

实体框架不允许您删除尚未加载(或附加)的对象。这也扩展到条件删除(例如删除所有名为 John 的用户),因为它要求您在删除用户之前加载用户。

您可以通过执行原始 SQL 来解决这个问题。这并不理想,因为您倾向于使用 EF,因此您不必编写 SQL,但缺乏适当的删除行为(不加载)使得这是一个可以接受的解决方案。

大致如下:

using (var context = new FooContext())
{
    var command = "DELETE * FROM dbo.Foos WHERE Id = 1";

    context
        .Database
        .ExecuteSqlCommand(command);
} 
Run Code Online (Sandbox Code Playgroud)

在相关的情况下,不要忘记 SQL 注入保护。然而,对于简单的删除来说,这通常不是问题,因为 FK 通常是 GUID 或 int,这不会让您遭受注入攻击。


使其通用

您发布的示例也有效,但您可能没有使用它,因为它不容易变得通用友好。

我在所有 EF 项目中倾向于为所有实体创建一个(抽象)基类,大致如下:

public class BaseEntity
{
    public int Id { get; set; }

    public DateTime CreatedOn { get; set; }
    public string CreatedBy { get; set; }

    public DateTime? UpdatedOn { get; set; }
    public string UpdatedBy { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

接口也可以,我只是更喜欢这里的基类。

审计字段不是这个答案的一部分,但它们确实展示了拥有基类的好处。

当所有实体继承自同一基类时,您可以在存储库上放置泛型类型约束,以确保泛型类型具有属性Id

public class IRepositoy<TEntity> : IRepository<TEntity> where TEntity : BaseEntity
Run Code Online (Sandbox Code Playgroud)

此时您可以普遍实施您提出的解决方案:

public void Remove(TEntity obj)
{
    dbContext.Entry(obj).State = EntityState.Deleted;
}
Run Code Online (Sandbox Code Playgroud)

您还可以指定无参数构造函数类型约束:

where TEntity : BaseEntity, new()
Run Code Online (Sandbox Code Playgroud)

这也使您能够实例化您的泛型类型:

public void Remove(int id)
{
    TEntity obj = new TEntity() { Id = id };
    dbContext.Entry(obj).State = EntityState.Deleted;
}
Run Code Online (Sandbox Code Playgroud)

注意
还有一个通用的原始 SQL 解决方案,但我省略了它,因为它更复杂,因为它要求您根据实体类型检索表名。
原始 SQL 变体仅在您想要执行条件删除的情况下才有价值(例如,删除 id 为偶数的所有实体)。

然而,由于大多数条件删除都是特定于实体的,这意味着您通常不需要使它们通用,这使得原始 SQL 方法更加可行,因为您只需在特定存储库而不是通用存储库中实现它。