Is there any way to mark an entity as read-only and not specify any key for it?
-
Code First and read only are kind of mutually exclusive. Just out of curiosity why would you not want a primary key? – Brian Feb 23 '12 at 15:01
-
2Enttity is mapped to a view and I don't want update/insert on it, an have no key on it either. – Otake Feb 23 '12 at 15:32
-
EF will not do updates on views by default. – Gert Arnold Feb 23 '12 at 16:00
-
@GertArnold, that is incorrect. You can update/insert views in EF. – Otake Feb 23 '12 at 16:29
-
Is this [information](http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/5fb8d970-1131-4de7-a7fa-6cd1d7839e84/) outdated then? Could be, EF develops pretty fast. – Gert Arnold Feb 23 '12 at 16:38
-
Hmm. [MSDN](http://msdn.microsoft.com/en-us/library/cc716779.aspx) also has it. It is the _default_. Which does not mean that there are no work-arounds. – Gert Arnold Feb 23 '12 at 16:42
-
I think it is outdated. You can manipulate the edm file in database first approach to do this or just map to a view in code first. It will send a nice query to db engine and if your view is updatable it will work. – Otake Feb 23 '12 at 16:48
4 Answers
There are a couple of things that you can do to enforce read-only in Code First. The first is to use AsNoTracking()
when you query.
var readOnlyPeople = (from p in context.People
where p.LastName == "Smith"
select p).AsNoTracking();
This tells Code First to not track changes to these entities, so when you call SaveChanges()
no changes made to these objects will be persisted.
The seccond thing you can do is set the state to Unchanged
before calling SaveChanges()
.
context.Entry(person).State = EntityState.Unchanged;
context.SaveChanges();
This tells Code First to ignore any changes that have been made to that entity.
As far as not having a key, all entities must have a key. This may not necessarily map to a primary key in the database, but it "must uniquely identify an entity type instance within an entity set".

- 28,825
- 9
- 92
- 117
-
2Hi Brice, your suggestions are not ideal solutions for me (I would really prefer just marking that entity as read-only - which exists in NH) but I don't think what I want is possible with a neat solution in EF yet. As per "all entities must have a key", I kind a agree with you but sometimes you have to work with some views and they don't have any key and If I can mark entitiy as read-only why should I need a key. – Otake Mar 09 '12 at 09:33
-
Actually you can use an IReadOnlyEntity marker interface similar to the ICacheableEntity interface suggested here (There are blog posts describing this as well were I originally found the code, but I couldn't find them) http://stackoverflow.com/a/6593261/34474 There are some gotcha's with relations between them that you should consider (If any one is interested let me know) In the end we are doing what Brice suggested only in a more automated way. – Cohen Dec 26 '12 at 11:33
-
If your view does not have a natural key, you can add one to help EntityFramework. In the view definition: SELECT NEWID() as [VirtualKey] ... In the Entity's Map: // Primary Key this.HasKey(t => t.VirtualKey); – Elton Feb 15 '17 at 16:38
Using code-first in EF6, I created some entities that reflect Views, which obviously shouldn't be modified or saved. To prevent the Entity from being changed, I used protected set properties:
public class TransplantCenterView
{
public string TransplantsThisYear { get; protected set; }
}
Entity Framework is still able to set this property, but other developers can't accidentally do it without a compile-time error. This works great, but it seems the better solution would be to eliminate tracking completely.
Thanks to reggaeguitar's answer, it appears there is an answer to this (please also vote his answer up if the following is helpful), which has allowed me to change my code from:
public class MyContext : DbContext
{
public DbSet<TransplantCenterView> TransplantCenterViews { get; set; }
}
To:
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(); }
}
}
I don't know of any pros and cons to this, but my existing code has continued working without any hitches, so seems a win.

- 1,219
- 15
- 24

- 7,613
- 4
- 31
- 42
-
-
Devs _could_ still call MyContext.Set
().DoAnyThing(), so I still prefer your `protected set` solution. – Steven Ryssaert Nov 13 '17 at 06:13
If you want the entire entity to be read-only you can do this
/// Using a dbquery since this is readonly.
/// </summary>
public DbQuery<State> States
{
get
{
// Don't track changes to query results
return Set<State>().AsNoTracking();
}
}
source http://www.adamtuliper.com/2012/12/read-only-entities-in-entity-framework.html

- 1,795
- 1
- 27
- 48
-
4Changed DbSet to DbQuery. Changed the getter to as described above. Compiled. All good. However, when I go to execute the page that loads data from the view I get a runtime error: "The entity type `MyEntityName` is not part of the model for the current context." So, added `protected DbSet
_hiddenMyEntitiesName {get; set;}` and then things worked. It would be worthwhile to add the DbSet<> into your code above so people don't run into the same confusion...or if there's another way? But thanks for putting me on the right path. – Kevin Nelson Aug 16 '17 at 19:58 -
Interesting, I didn't get that error. I'm mapping to tables and not views, maybe that's the difference? – reggaeguitar Aug 17 '17 at 16:41
-
Hmm...so, you don't have a DbSet
property anywhere in your context, and it still works? Odd...could also be a version issue. I'm back on VS 2015 and EF6. – Kevin Nelson Aug 17 '17 at 18:56 -
I don't have DbSet
only DbQuery – reggaeguitar Aug 18 '17 at 18:54and my old LINQ queries work fine. I'm also on VS 2015 EF6 -
Rather than add the ***protected DbSet
_hiddenMyEntitiesName {get; set;}*** to your context add ***modelBuilder.Entity – John S Dec 20 '17 at 16:04();*** to the OnModelCreating method. -
You can also make it read-only as a more "global" rule for a specific entity by type. Simply override the appropriate SaveChanges*
method and set the entity's state if you see that EF is trying to add it during the save.
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);
}

- 26
- 3