【问题标题】:Entity Framework Code First ReadOnly Entity实体框架代码优先只读实体
【发布时间】:2012-02-23 14:50:11
【问题描述】:

有没有办法将实体标记为只读而不指定任何键?

【问题讨论】:

  • 代码优先和只读是相互排斥的。只是出于好奇,您为什么不想要主键?
  • 实体被映射到一个视图,我不想更新/插入它,也没有键。
  • EF 默认不会更新视图。
  • @GertArnold,这是不正确的。您可以在 EF 中更新/插入视图。
  • information 过时了吗?可能,EF 发展很快。

标签: entity-framework ef-code-first


【解决方案1】:

您可以做几件事来在 Code First 中强制只读。第一种是查询时使用AsNoTracking()

var readOnlyPeople = (from p in context.People
                        where p.LastName == "Smith"
                        select p).AsNoTracking();

这告诉 Code First 不要跟踪对这些实体的更改,因此当您调用 SaveChanges() 时,对这些对象所做的更改不会被持久化。

您可以做的第二件事是在调用SaveChanges() 之前将状态设置为Unchanged

context.Entry(person).State = EntityState.Unchanged;
context.SaveChanges();

这告诉 Code First 忽略对该实体所做的任何更改。

只要没有密钥,所有实体都必须有密钥。这可能不一定映射到数据库中的主键,但它“必须唯一标识实体集中的实体类型实例”。

【讨论】:

  • 嗨 Brice,你的建议对我来说不是理想的解决方案(我真的更喜欢将该实体标记为只读 - 它存在于 NH 中)但我认为我想要的不可能EF 中的一个巧妙的解决方案。根据“所有实体都必须有一个密钥”,我同意你的观点,但有时你必须使用一些视图并且它们没有任何密钥,如果我可以将实体标记为只读,为什么我需要一个密钥.
  • 实际上你可以使用类似于这里建议的 ICacheableEntity 接口的 IReadOnlyEntity 标记接口(也有博客文章描述了这一点,我最初找到了代码,但我找不到它们)stackoverflow.com/a/6593261/34474您应该考虑它们之间的一些关系问题(如果有人感兴趣,请告诉我)最后,我们只会以更自动化的方式做 Brice 建议的事情。
  • 如果你的视图没有自然键,你可以添加一个来帮助EntityFramework。在视图定义中:SELECT NEWID() as [VirtualKey] ... 在实体的 Map 中://主键 this.HasKey(t => t.VirtualKey);
【解决方案2】:

在 EF6 中使用代码优先,我创建了一些反映视图的实体,这些实体显然不应该被修改或保存。为了防止实体被更改,我使用了受保护的集合属性:

public class TransplantCenterView
{
    public string TransplantsThisYear { get; protected set; }
}

Entity Framework 仍然可以设置此属性,但其他开发人员无法在没有编译时错误的情况下意外执行此操作。这很好用,但似乎更好的解决方案是完全消除跟踪。


感谢reggaeguitar's answer,似乎有一个答案(如果以下内容有帮助,也请投票给他的答案),这使我可以更改我的代码:

public class MyContext : DbContext
{
    public DbSet<TransplantCenterView> TransplantCenterViews { get; set; }
}

收件人:

public class MyContext : DbContext
{
    // appears the DbSet is still needed to make Set<Entity>() work
    protected DbSet<TransplantCenterView> _transplantCenterViews { get; set; }
    // this .AsNoTracking() disables tracking for our DbSet.
    public DbQuery<TransplantCenterView> TransplantCenterViews
    {
        get { return Set<TransplantCenterView>().AsNoTracking(); }
    }
}

我不知道这有什么利弊,但我现有的代码可以继续正常工作,所以看起来很成功。

【讨论】:

  • 查看我的答案,了解禁用实体跟踪的方法。
  • 开发者可以仍然调用 MyContext.Set().DoAnyThing(),所以我还是更喜欢你的protected set 解决方案。
【解决方案3】:

如果你希望整个实体是只读的,你可以这样做

/// Using a dbquery since this is readonly.
/// </summary>
public DbQuery<State> States
{
  get
  {
    // Don't track changes to query results
    return Set<State>().AsNoTracking();
 }
}

来源http://www.adamtuliper.com/2012/12/read-only-entities-in-entity-framework.html

【讨论】:

  • 已将 DbSet 更改为 DbQuery。将吸气剂更改为如上所述。编译。都好。但是,当我执行从视图加载数据的页面时,会出现运行时错误:“实体类型 MyEntityName 不是当前上下文模型的一部分。”因此,添加了protected DbSet&lt;MyEntityName&gt; _hiddenMyEntitiesName {get; set;},然后一切正常。将 DbSet 添加到上面的代码中是值得的,这样人们就不会遇到同样的困惑……或者是否还有其他方法?但是感谢您让我走上正确的道路。
  • 有趣,我没有得到那个错误。我映射到表而不是视图,也许这就是区别?
  • 嗯...所以,您的上下文中的任何地方都没有 DbSet 属性,它仍然有效吗?奇怪...也可能是版本问题。我回到了 VS 2015 和 EF6。
  • 我没有 DbSet 只有 DbQuery 并且我的旧 LINQ 查询工作正常。我也在 VS 2015 EF6
  • 而不是添加 protected DbSet _hiddenMyEntitiesName {get; set;}modelBuilder.Entity(); 添加到 OnModelCreating 方法。
【解决方案4】:

您还可以将其设为只读,作为按类型针对特定实体的更“全局”规则。如果您看到 EF 在保存期间尝试添加它,只需覆盖适当的 SaveChanges* 方法并设置实体的状态。

public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
    foreach (var entry in ChangeTracker.Entries())
    {
        if (entry.Entity.GetType() == typeof(<YOUR_TYPE_HERE>) && entry.State == EntityState.Added)
        {
            entry.State = EntityState.Detached;
        }
    }

    return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-01
    • 2014-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-29
    相关资源
    最近更新 更多