【问题标题】:Dynamic filters in Entity Framework do not filter out entities when reading them right after updatingEntity Framework 中的动态过滤器在更新后立即读取实体时不会过滤掉它们
【发布时间】:2018-04-11 11:39:00
【问题描述】:

我在实体框架中使用动态过滤器。它是EntityFramework.DynamicFilters 中的DBModelBuilder.Filter 扩展方法。当我修改要过滤掉的实体时,在使用相同数据库上下文的下一个请求中,它在浏览导航集合时仍然可见。

这是正常行为吗?有什么方法可以在不改变应用程序中更新和读取数据的方式的情况下修复它?

以下是显示我的问题的示例控制台应用程序。

当我使用 ProductDbContext 的不同实例通过类别间接读取产品时,或者当我直接从同一数据库上下文中读取类别时,它工作正常。

实体和数据库上下文:

public enum Status
{
    New = 0,
    Used = 1
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Status Status { get; set; }

    public int CategoryId { get; set; }
    public virtual Category Category { get; set; }

    public override string ToString()
    {
        return $"Id = {Id}, Name = {Name}, Status = {Status.ToString()}";
    }
}

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class ProductDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Filter("OnlyNewProducts", (Product p) => p.Status == Status.New);
    }
}

类别:

-------------------
| Id | Name       |
-------------------
| 3  | Category 1 |
-------------------

产品:

----------------------------------------
| Id | Name      | Status | CategoryId |
----------------------------------------
| 3  | Product 1 | 0      | 3          |
----------------------------------------

显示我的问题的类:

class Program
{
    static ProductDbContext ctx = new ProductDbContext();

    static void Main(string[] args)
    {
        Read();
        Modify();
        Read();
    }

    private static void Modify()
    {
        var product = ctx.Products.FirstOrDefault(p => p.Name == "Product 1");
        product.Status = Status.Used;
        ctx.Entry(product).State = EntityState.Modified;
        ctx.SaveChanges();
    }

    private static void Read()
    {
        var newProducts = ctx.Categories.FirstOrDefault(c => c.Name == "Category 1").Products.ToList();

        foreach (var newProduct in newProducts)
        {
            Console.WriteLine(newProduct.ToString());
        }
    }
}

结果:

Id = 3, Name = Product 1, Status = New 
Id = 3, Name = Product 1, Status = Used

不应显示具有“已使用”状态的更新实体。

【问题讨论】:

  • 我在保存更改后通过调用 ((IObjectContextAdapter)ctx).ObjectContext.Refresh(RefreshMode.StoreWins, product) 找到了解决方法,但这不是我想要的。

标签: c# .net entity-framework filter dbcontext


【解决方案1】:

注意:这就是 Linq 的工作方式。我对 Entity 没有经验。

有点像 Cedric 之前发布的内容和 [alcohol is evil] 的评论:

private static void Modify()
{
    var product = ctx.Products.FirstOrDefault(p => p.Name == "Product 1");
    product.Status = Status.Used;
    ctx.Entry(product).State = EntityState.Modified;
    ctx.SaveChanges();
    ctx.Refresh(RefreshMode.OverwriteCurrentValues, product);
    product = ctx.Products.FirstOrDefault(p => p.Name == "Product 1");
}

我没有使用实体,但这在 Linq 中有效。

刷新(同样,在 Linq 中)被重载以接受 product 或跳过它。我发现如果我跳过它,那么我必须重新加载product,如下行所示。

希望对你有帮助。

【讨论】:

  • jp2code,Refresh 方法是 DbContext 类的扩展方法吗?它需要参考其他图书馆吗?无论如何,在您的示例中,不需要刷新,因为直接从 DbContext 读取修改后的产品没有问题。问题是通过其他类的导航属性读取修改后的产品时。
  • Refresh 是 DataContext 类的成员。它有很多选项。我不确定它是否会起作用,但它可能会为您提供有关如何更新控件的想法。
【解决方案2】:

起初,没有必要这样做:

ctx.Entry(product).State = EntityState.Modified;

在这种情况下,EF 自动将实体标记为已修改(来自上下文,因此它是代理对象)。

经过多次测试,我重现了您的行为。它不是来自 EF,而是来自动态过滤器。 我发现的唯一方法是重新创建上下文。

static void Main(string[] args)
{
    Read();
    Modify();
    ctx = new Product2DbContext();
    Read();
}

这不漂亮,我知道。 您可以在此处添加问题:https://github.com/zzzprojects/EntityFramework.DynamicFilters/issues

其他编辑: 经查,动态过滤器直接使用Interceptor重写de SQL Request。 当您第一次在类别中加载您的产品时,拦截器会完成它的工作,因为它会发送一条 SQL 命令。 第二次,当您尝试访问产品时,您的上下文中已经知道类别并且那里的产品也是如此。所以 EF 不需要执行查询(LazyLoading 行为)。所以拦截器没有被调用。 我不知道你是否可以强制刷新数据,我正在尝试没有成功。

【讨论】:

  • 好的,我知道你的意思。但我使用的是EF。我不会使用 SQL 命令或 EF 之外的任何其他方式修改任何记录。对我来说,它看起来像是 EF 中的一个错误(如果我的问题没有解决方案)。
  • 在我看来,这不是一个错误。它不是 ORM 的目标。您可以创建包含所有数据或仅更改的模式。当您查询该数据时,您可以检查您的 SQL 服务器和您更改的数据并合并所有以提取您想要的内容。必须尽可能缩短上下文生命周期。
  • @alcoholisevil 为什么您认为这是 EF 错误,而不是您正在使用的 3rd 方 软件包的错误/限制? EF 不支持过滤器,所以我不明白你的意思。
  • @IvanStoev,你是对的。这不是 EF 错误,但我仍然认为这是一个错误(或您建议的限制)。如果我想查看更新的实体,我可以轻松禁用过滤器。当我定义了全局过滤器时,我无法想象再次过滤每个读取的数据。在我的示例应用程序中,这很容易,但在具有存储库和服务的 Web 应用程序中,对整个 HTTP 请求使用所有相同的 DbContext 实例。
  • @alcoholisevil 我只是说这是EntityFramework.DynamicFilters 库的问题/错误/限制。开源软件的风险。
猜你喜欢
  • 2012-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多