Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is EntityState enumeration defined with FlagsAttribute

I'm doing a simple Entity change logging for our application. One question that arises is that while the enumeration values for DbEntityEntry.State property are clearly mutually exclusive (See MSDN), it is defined with Flags attribute and the values are chosen as if they can be combined.

Is it safe to assume that the values are mutually exclusive? Why did they choose this path?

like image 797
Alireza Avatar asked Oct 01 '15 09:10

Alireza


2 Answers

Is it safe to assume that the values are mutually exclusive?

If you are planning for future compatibility, then no. The authors might add a value which could be combined with one of the current values to cover a wider state. You are better off masking.

They might not, but being defined this way leaves that option open.

Why did they chose this path?

Perhaps, so that people wouldn't assume that the values are mutually exclusive.

Mostly, it allows one to do mask tests that cover more than one value in a single test:

if (state & (EntityState.Deleted | EntityState.Modified | EntityState.Added) != 0)  …

This works because the values for the enum uses a flag layout style where each has different bits set (or is deliberately defined in terms of another):

Detached = 1,
Unchanged = 2,
Added = 4,
Deleted = 8,
Modified = 16

Because of this, EntityState.Deleted | EntityState.Modified | EntityState.Added has a value of 8 | 16 | 4 which is 28. It would not work if the values where just incremented:

Detached = 1,
Unchanged = 2,
Added = 3,
Deleted = 4,
Modified = 5

Now EntityState.Deleted | EntityState.Modified | EntityState.Added would have a value of 7 which is the same as EntityState.Detached | Entity.Unchanged | Entity.Deleted.

The FlagsAttribute is used to provide metadata indicating you are taking the former approach rather than the latter, so masking can work. The only effect it has directly on the type itself is in how ToString() works, which is to provide a value more meaningful when taking this approach than not.

like image 62
Jon Hanna Avatar answered Nov 18 '22 01:11

Jon Hanna


Internally, EntityState is also used to include/exclude more than one state.

For example, in ObjectStateManager:

IEnumerable<IEntityStateEntry> IEntityStateManager.GetEntityStateEntries(EntityState state)

Usage in ObjectContext:

var entriesAffected = ObjectStateManager.GetObjectStateEntriesCount(EntityState.Added |                   
                                                                    EntityState.Deleted |
                                                                    EntityState.Modified);

From API-consuming-point-of-view, I think you can safely assume that the actual state of an entity can only be one of the options.

like image 37
haim770 Avatar answered Nov 18 '22 03:11

haim770