1

A bit of a mystery has popped up with EF5 and non-null strings in the schema. For example, say we have this simple user table:

CREATE TABLE Users
(
   UserID INT PRIMARY KEY NOT NULL IDENTITY(1,1),
   UserName VARCHAR(50) NOT NULL,
   UserFirst VARCHAR(50) NULL,
   UserLast VARCHAR(50) NULL
)

... and I create a POCO class to represent it. This all works fine. I can retrieve and create data, however update has caused some serious pain. Say for a moment that I want to update a given user's first name:

User user = new User { UserID = 12345 };
context.Users.Attach(user);
user.FirstName = "SomethingElse";
context.SaveChanges();

This should work. After attachment, the non-nullable UserName is null. Which should be fine - I'm not trying to change it, and the change detection states that the value is unchanged. However, when I call SaveChanges, a DB validation exception is thrown stating the UserName is invalid.

So, from this, two questions:

  1. Am I doing something egregiously wrong? I'm entirely willing to believe my understanding of entity framework is flawed.
  2. Why is the change state by property not used at save-time? I'm aware of context.Configuration.ValidateOnSaveEnabled, but I don't think simply turning validation off is a valid answer. Is there another similar option I can lean on?
Ross
  • 2,448
  • 1
  • 21
  • 24

1 Answers1

2

You say that you're wanting to update a user, but your example code is creating a new User, with an Id of 12345, and then attaching it. What your code is doing is creating a brand new entity, leaving a non-nullable field null, and attempting to save to the database, which throws the error.

Are you intending to retrieve an existing user w/ an ID of 12345? Then instead of creating a new entity, you should fetch that entity from the database, and then update the property.

User user = context.Users.First(u => u.UserID == 12345);
user.FirstName = "SomethingElse";
context.SaveChanges();

EDIT

What's happening is when you modify the FirstName property after the attach, EF marks the entire entity as dirty and so it attemps to update everything.

If you want to update just the FirstName you'll need to alter your code (From a related SO post, How to update a single column):

  var user = new User { UserID = 12345, FirstName = "SomethingElse" };
  using (var db = new MyEfContextName())
  {
    context.Users.Attach(user);
    context.Entry(user).Property(x => x.FirstName).IsModified = true;
    context.SaveChanges();
  }
Community
  • 1
  • 1
Mark Oreta
  • 10,346
  • 1
  • 33
  • 36
  • I understand what you're getting at, but I respectfully disagree. From the documentation of `Attach`: "... the entity is placed into the Unchanged state, just as if it had been read from the database." In other words, the fact `UserName` is null _should_ be immaterial, and by extension, the validation should be by property, not by the object. (Additionally, if you set this same scenario up but set the default to empty string in the EDMX, you need not set `UserName` at save-time and empty string is not persisted.) – Ross Sep 25 '12 at 14:23
  • What wasn't immediately clear to me was why the entity was marked as dirty wholesale when the entity explicitly exposes `IsModified`. In my mind, only `IsModified` fields should be examined, and in my original case, only one field _was_ modified. However, after thinking about it, trying to make the context smart enough to save entities piecemeal - especially from a code-first standpoint - would be pretty daunting. It's doable with clever templating, but I'm not certain that it's worth the effort for this project. – Ross Sep 25 '12 at 15:03