如何使Readity实体框架数据上下文

Har*_*aka 103 .net datacontext entity-framework readonly entity-framework-4

我需要向第三方插件公开实体框架数据上下文.目的是允许这些插件仅获取数据,而不是让它们发出插入,更新或删除或任何其他数据库修改命令.因此,我如何只读取数据上下文或实体.

bri*_*lam 166

除了与只读用户连接外,还可以对DbContext执行一些其他操作.

public class MyReadOnlyContext : DbContext
{
    // Use ReadOnlyConnectionString from App/Web.config
    public MyContext()
        : base("Name=ReadOnlyConnectionString")
    {
    }

    // Don't expose Add(), Remove(), etc.
    public DbQuery<Customer> Customers
    {
        get
        {
            // Don't track changes to query results
            return Set<Customer>().AsNoTracking();
        }
    }

    public override int SaveChanges()
    {
        // Throw if they try to call this
        throw new InvalidOperationException("This context is read-only.");
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Need this since there is no DbSet<Customer> property
        modelBuilder.Entity<Customer>();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,使用`AsNoTracking()`将无法使用延迟加载. (6认同)
  • 不要依赖于此,因为`(context as IObjectContextAdapter).ObjectContext.SaveChanges()`仍然有效.最好的选择是使用`DbContext(string nameOrConnectionString);`contstructor,带有用于数据库创建内容的读/写连接字符串,以及之后的只读连接字符串. (6认同)
  • @bricelam 在 EntityFrameworkCore 中,它应该是“public IQueryable&lt;Customer&gt; Customers =&gt; Set&lt;Customer&gt;().AsNoTracking();” (4认同)
  • 不要忘记重写`public override Task <int> SaveChangesAsync()`. (3认同)

Ehs*_*edi 16

与公认的答案相反,我相信最好是继承而不是继承。这样就无需保留诸如SaveChanges之类的方法来引发异常。此外,为什么首先需要使用这种方法?您应该以一种让其使用者在查看其方法列表时不会上当的方式设计类。公共接口应与类的实际意图和目标保持一致,而在接受的答案中,具有SaveChanges并不意味着Context是只读的。

在需要具有只读上下文的地方,例如CQRS模式的“读取”端,我使用以下实现。除了向使用者提供查询功能外,它没有提供任何其他功能。

public class ReadOnlyDataContext
{
    private readonly DbContext _dbContext;

    public ReadOnlyDataContext(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public IQueryable<TEntity> Set<TEntity>() where TEntity : class
    {
        return _dbContext.Set<TEntity>().AsNoTracking();
    }

    public void Dispose()
    {
        _dbContext.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

通过使用ReadOnlyDataContext,您只能访问DbContext的查询功能。假设您有一个名为Order的实体,那么您将以如下方式使用ReadOnlyDataContext实例。

readOnlyDataContext.Set<Order>().Where(q=> q.Status==OrderStatus.Delivered).ToArray();
Run Code Online (Sandbox Code Playgroud)

  • 并使其继承自`IDisposable` (3认同)
  • @hkarask - 不确定我会这样做。由于此调用没有创建 DbContext,因此不应释放它。这可能会导致以后出现一些难以追踪的错误。 (2认同)

小智 7

在我使用 EF Core/.NET 5.0 的场景中,我希望 SaveChanges 具有编译时安全性。这只适用于“新”而不是“覆盖”。

我并排使用读/写和只读上下文,其中一个上下文继承另一个上下文,因为附加了很多表。这就是我使用的,“ContextData”是我原来的 R/W DbContext:

public class ContextDataReadOnly : ContextData
{
    public ContextDataReadOnly() : base()
    {
        ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
    }

    [Obsolete("This context is read-only", true)]
    public new int SaveChanges()
    {
        throw new InvalidOperationException("This context is read-only.");
    }

    [Obsolete("This context is read-only", true)]
    public new int SaveChanges(bool acceptAll)
    {
        throw new InvalidOperationException("This context is read-only.");
    }

    [Obsolete("This context is read-only", true)]
    public new Task<int> SaveChangesAsync(CancellationToken token = default)
    {
        throw new InvalidOperationException("This context is read-only.");
    }

    [Obsolete("This context is read-only", true)]
    public new Task<int> SaveChangesAsync(bool acceptAll, CancellationToken token = default)
    {
        throw new InvalidOperationException("This context is read-only.");
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:

  • 在覆盖继承的 SaveChanges*() 时,我必须使用“new”而不是“override”,以便出现警告/错误。使用“覆盖”,根本没有编译时错误/警告。

  • 使用“override”你会得到CS0809 [1],但使用“new”则不会

  • 使用“new”仅适用于类本身,但不适用于父类的上下文:

    Base b = new Derived();
    Derived d = new Derived();
    
    b.SaveChanges();     // Calls Base.SaveChanges, will compile and run without exception
    d.SaveChanges();     // Calls Derived.SaveChanges, will not compile
    
    Run Code Online (Sandbox Code Playgroud)
  • SaveChanges 和 SaveChangesAsync 的变体需要正确选择(可选)参数。(这是针对.NET 5.0的,我没有检查它是否与其他版本的EF Core/EF不同)

结论

  1. “覆盖”将提供完全继承,但在我的环境中不起作用
  2. “new”提供了所需的功能,但对于某些多态场景会返回意外的结果
  3. 如果你有很多表,根本不使用继承会很痛苦

==> 没有灵丹妙药,选择取决于品味和环境......

[1] https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs0809?f1url=%3FappId%3Droslyn%26k%3Dk(CS0809)