c# - 删除实体而不在通用存储库模式 Entity Framework 中获取它

标签 c# sql entity-framework generics repository-pattern

我正在尝试使用通用存储库模式从包含不同表(如员工、项目、技能)的数据库中删除员工实体。

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);
        }
    }
}

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

  1. 一个用于获取实体。

  2. 其次是删除它。

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

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

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

最佳答案

使用原始 SQL

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

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

类似的东西:

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

    context
        .Database
        .ExecuteSqlCommand(command);
} 

在相关的地方,不要忘记 SQL 注入(inject)保护。但是,对于简单的删除,这通常不是问题,因为 FK 通常是 GUID 或 int,这不会使您遭受注入(inject)攻击。


让它变得通用

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

我倾向于在我的所有 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; }
}

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

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

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

public class IRepositoy<TEntity> : IRepository<TEntity> where TEntity : BaseEntity

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

public void Remove(TEntity obj)
{
    dbContext.Entry(obj).State = EntityState.Deleted;
}

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

where TEntity : BaseEntity, new()

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

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

Note
There is a generic raw SQL solution as well, but I've omitted it as it is more complex because it requires you to retrieve the table name based on the entity type.
The raw SQL variant is only valuable in cases where you want to execute conditional deletes (e.g. removing all entities whose id is an even number).

However, since most conditional deletes are entity-specific, this means that you generally don't need to make them generic, which makes the raw SQL approach more viable as you only have to implement it in a specific repository and not the generic one.

关于c# - 删除实体而不在通用存储库模式 Entity Framework 中获取它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57870587/

相关文章:

Java-Sqlite 截断所有数据库表

c# - Entity Framework 核心 : How to configure model with two One to Many relations in one entity referencing the same table

.net - 如何将 ObjectStateEntry.OriginalValues 转换为实体?

entity-framework - 分配关联时停止 EF 加载实体

c# - 在导航栏中显示模态

sql - GROUP BY 子句未显示所需结果

c# - 如何在 Windows 和 IANA 时区之间进行转换?

mysql - 查询 MS Access 以插入记录(如果不存在)

c# - SQL Server 中的 CLR 存储过程的性能

c# - as 关键字可以代替 try/catch block 吗?