4

I'm working with .NET C# and Entity Framework.

I have a situation where I need to update an object in the database but I'll keep one property untouched. I'd like to know is it possible to do that?

Take the following class for example:

public class Person
{
    public int PersonId { get; set; }
    public string Name{ get; set; }
    public int Age { get; set; }
    public DateTime CreationDate { get; set; }
}

So I would provide a HTML form in a page for the client to edit a person. Example:

<form>
    <input type="hidden" id="PersonId" value="1" />
    <input type="text" id="Name" value="Cesar" />
    <input type="text" id="Age " value="28" />
</form>

Then I'd grab the values and send to the repository to update that Person. But it does not make sense updating CreationDate, and I don't want to provide that possibility.

So I could do it in 2 different ways.

1) I could put it in the form as a hidden field and post it back. (but as I said I don't want to provide that possibility)

2) In the repository get the Person, update it's fields and leave CreationDate date the same and then save it:

void UpdatePerson(Person p){
    var person = db.Persons.Find(PersonId);

    person.Name = p.Name;
    person.Age = p.Age;

    db.Persons.Attach(person);
    db.Entry(person).State = EntityState.Modified;
    db.SaveChanges();        
}

I'd like to know a way that I could just pass "p" to the context and update it leaving CreationDate the way it is.

I hope I was clearer.

I'm sorry about the lack of information in the first post.

Thank you for your help.

Cesar Zapata
  • 196
  • 1
  • 7
  • 15
  • 2
    Yes it is, but unless you show some code I'm afraid that no one will be able to fully help you... – Corey Adler Dec 03 '13 at 14:37
  • 2
    more research required. You will find many SO posts with answers covering Update Set and EF. what did you test that didnt work ? – phil soady Dec 03 '13 at 15:08
  • EF will only update fields that have changed. If you just don't change that field, no update will happen. – Scottie Dec 03 '13 at 22:43
  • 1
    You just ran into one of the reasons you should not use entity models as view models. The solution is your second suggestion, mapping columns. AutoMapper can help there. Anyway see also [How to avoid certain fields to be updated in model](http://stackoverflow.com/questions/17008583/), [Ignore certain columns on update](http://stackoverflow.com/questions/18564127/), [Ignore optional columns on save](http://stackoverflow.com/questions/6076167/), [and so on](https://www.google.com/search?q=entity+framework+ignore+columns). – CodeCaster Dec 03 '13 at 22:57

4 Answers4

4

Personally, I use AutoMapper and use Ignore on the one field you don't want to copy. This way, if you ever add new columns to the table, you don't have to worry about adding them to your repository.

So, something like this...

void UpdatePerson(Person p) {
    var person = db.Persons.Find(PersonId);

    Mapper.CreateMap<Person, Person>()
        .ForMember(dest => dest.CreationDate, opt => opt.Ignore());
    Mapper.Map(p, person);        

    db.SaveChanges();        
}
Scottie
  • 11,050
  • 19
  • 68
  • 109
1

Two other methods...

add a default to your SQL table

ALTER TABLE dbo.Person ADD CONSTRAINT
DF_Person_CreatedDate DEFAULT GetDate() FOR CreatedDate

use a backing field and default that to DateTime.Now

    public DateTime CreatedDate
    {
        get { return createdDate; }
        set { createdDate = value); }
    }

    private DateTime createdDate = DateTime.Now;
ajd
  • 423
  • 4
  • 11
  • I think you don't want that second suggestion, as it'll always return the current date, which most often was not the time the object was created. – CodeCaster Dec 03 '13 at 22:58
  • 2
    ...unless of course you load the entity from your database when the default will be overwritten with the stored value... – ajd Dec 04 '13 at 09:05
0

This is what I use, using custom InjectNonNull (obj dest, obj src) it makes it fully flexible, even if you have two or more fields

[HttpPost]
public async Task<IActionResult> Post( [FromQuery]Models.Currency currency ) {
  if ( ModelState.IsValid ) {
    // find existing object by Key
    Models.Currency currencyDest = context.Currencies.Find( currency.Id ); 

    context.Currencies.Attach( currencyDest );

    // update only not null fields
    InjectNonNull( currencyDest, currency );

    // save
    await context.SaveChangesAsync( );
  }  
  return Ok();
}

// Custom method
public static T InjectNonNull<T>( T dest, T src ) {
  foreach ( var propertyPair in PropertyLister<T, T>.PropertyMap ) {
    var fromValue = propertyPair.Item2.GetValue( src, null );
    if ( fromValue != null && propertyPair.Item1.CanWrite ) {
       propertyPair.Item1.SetValue( dest, fromValue, null );
    }
  }
  return dest;
}
697
  • 321
  • 4
  • 16
0

For me is working fine this solution:

void UpdatePerson(Person p){
var person = db.Persons.AsNoTracking().Find(PersonId);//Use AsNoTracking

person.Name = p.Name;
person.Age = p.Age;

db.Persons.Attach(person);
db.Entry(person).State = EntityState.Modified;
db.SaveChanges();        

}

Microlang
  • 534
  • 4
  • 7