How to add calculated value into this TryUpdateModelAsync










4















I have this following code:



if (await TryUpdateModelAsync<Host>(
hostToUpdate,
"Host",
s => s.Name, s => s.Description, s => s.Address, s => s.Postcode, s => s.Suburb,
s => s.State))

try

await _context.SaveChangesAsync();
return RedirectToPage("./Index");

catch (DbUpdateConcurrencyException ex)

var exceptionEntry = ex.Entries.Single();
var clientValues = (Host)exceptionEntry.Entity;
var databaseEntry = exceptionEntry.GetDatabaseValues();
if (databaseEntry == null)

ModelState.AddModelError(string.Empty, "Unable to save. " +
"The host was deleted by another user.");
return Page();


var dbValues = (Host)databaseEntry.ToObject();
await setDbErrorMessage(dbValues, clientValues, _context);

// Save the current RowVersion so next postback
// matches unless an new concurrency issue happens.
Host.RowVersion = (byte)dbValues.RowVersion;
// Must clear the model error for the next postback.
ModelState.Remove("Host.RowVersion");




I have a properties for Host called: LastDateModified and LastModified which is calculated/predefined value



ie DateTime.Now for LastDateModified and _userManager.GetUserId(User) for LastDateModifiedBy.



So how do I pass this into this code?



await TryUpdateModelAsync<Host>(
hostToUpdate,
"Host",
s => s.Name, s => s.Description, s => s.Address, s => s.Postcode, s => s.Suburb,
s => s.State)









share|improve this question
























  • Is this question missing an asp.net-core tag?

    – n0rd
    Nov 17 '18 at 20:40












  • Thanks I just add them in.

    – dcpartners
    Nov 17 '18 at 20:42











  • Is it OK to call TryUpdateModelAsync twice? Why don't you just set the properties to their expected values, especially, since values are not coming from the request?

    – n0rd
    Nov 17 '18 at 21:05











  • @n0rd what do mean? How to pass the calculate value into this?

    – dcpartners
    Nov 17 '18 at 21:52











  • hostToUpdate.LastModified = DateTime.Now. Why do you need to pass anything anywhere?

    – n0rd
    Nov 17 '18 at 21:56
















4















I have this following code:



if (await TryUpdateModelAsync<Host>(
hostToUpdate,
"Host",
s => s.Name, s => s.Description, s => s.Address, s => s.Postcode, s => s.Suburb,
s => s.State))

try

await _context.SaveChangesAsync();
return RedirectToPage("./Index");

catch (DbUpdateConcurrencyException ex)

var exceptionEntry = ex.Entries.Single();
var clientValues = (Host)exceptionEntry.Entity;
var databaseEntry = exceptionEntry.GetDatabaseValues();
if (databaseEntry == null)

ModelState.AddModelError(string.Empty, "Unable to save. " +
"The host was deleted by another user.");
return Page();


var dbValues = (Host)databaseEntry.ToObject();
await setDbErrorMessage(dbValues, clientValues, _context);

// Save the current RowVersion so next postback
// matches unless an new concurrency issue happens.
Host.RowVersion = (byte)dbValues.RowVersion;
// Must clear the model error for the next postback.
ModelState.Remove("Host.RowVersion");




I have a properties for Host called: LastDateModified and LastModified which is calculated/predefined value



ie DateTime.Now for LastDateModified and _userManager.GetUserId(User) for LastDateModifiedBy.



So how do I pass this into this code?



await TryUpdateModelAsync<Host>(
hostToUpdate,
"Host",
s => s.Name, s => s.Description, s => s.Address, s => s.Postcode, s => s.Suburb,
s => s.State)









share|improve this question
























  • Is this question missing an asp.net-core tag?

    – n0rd
    Nov 17 '18 at 20:40












  • Thanks I just add them in.

    – dcpartners
    Nov 17 '18 at 20:42











  • Is it OK to call TryUpdateModelAsync twice? Why don't you just set the properties to their expected values, especially, since values are not coming from the request?

    – n0rd
    Nov 17 '18 at 21:05











  • @n0rd what do mean? How to pass the calculate value into this?

    – dcpartners
    Nov 17 '18 at 21:52











  • hostToUpdate.LastModified = DateTime.Now. Why do you need to pass anything anywhere?

    – n0rd
    Nov 17 '18 at 21:56














4












4








4


0






I have this following code:



if (await TryUpdateModelAsync<Host>(
hostToUpdate,
"Host",
s => s.Name, s => s.Description, s => s.Address, s => s.Postcode, s => s.Suburb,
s => s.State))

try

await _context.SaveChangesAsync();
return RedirectToPage("./Index");

catch (DbUpdateConcurrencyException ex)

var exceptionEntry = ex.Entries.Single();
var clientValues = (Host)exceptionEntry.Entity;
var databaseEntry = exceptionEntry.GetDatabaseValues();
if (databaseEntry == null)

ModelState.AddModelError(string.Empty, "Unable to save. " +
"The host was deleted by another user.");
return Page();


var dbValues = (Host)databaseEntry.ToObject();
await setDbErrorMessage(dbValues, clientValues, _context);

// Save the current RowVersion so next postback
// matches unless an new concurrency issue happens.
Host.RowVersion = (byte)dbValues.RowVersion;
// Must clear the model error for the next postback.
ModelState.Remove("Host.RowVersion");




I have a properties for Host called: LastDateModified and LastModified which is calculated/predefined value



ie DateTime.Now for LastDateModified and _userManager.GetUserId(User) for LastDateModifiedBy.



So how do I pass this into this code?



await TryUpdateModelAsync<Host>(
hostToUpdate,
"Host",
s => s.Name, s => s.Description, s => s.Address, s => s.Postcode, s => s.Suburb,
s => s.State)









share|improve this question
















I have this following code:



if (await TryUpdateModelAsync<Host>(
hostToUpdate,
"Host",
s => s.Name, s => s.Description, s => s.Address, s => s.Postcode, s => s.Suburb,
s => s.State))

try

await _context.SaveChangesAsync();
return RedirectToPage("./Index");

catch (DbUpdateConcurrencyException ex)

var exceptionEntry = ex.Entries.Single();
var clientValues = (Host)exceptionEntry.Entity;
var databaseEntry = exceptionEntry.GetDatabaseValues();
if (databaseEntry == null)

ModelState.AddModelError(string.Empty, "Unable to save. " +
"The host was deleted by another user.");
return Page();


var dbValues = (Host)databaseEntry.ToObject();
await setDbErrorMessage(dbValues, clientValues, _context);

// Save the current RowVersion so next postback
// matches unless an new concurrency issue happens.
Host.RowVersion = (byte)dbValues.RowVersion;
// Must clear the model error for the next postback.
ModelState.Remove("Host.RowVersion");




I have a properties for Host called: LastDateModified and LastModified which is calculated/predefined value



ie DateTime.Now for LastDateModified and _userManager.GetUserId(User) for LastDateModifiedBy.



So how do I pass this into this code?



await TryUpdateModelAsync<Host>(
hostToUpdate,
"Host",
s => s.Name, s => s.Description, s => s.Address, s => s.Postcode, s => s.Suburb,
s => s.State)






c# asp.net-core .net-core






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 17 '18 at 20:42







dcpartners

















asked Nov 14 '18 at 5:20









dcpartnersdcpartners

1,56783246




1,56783246












  • Is this question missing an asp.net-core tag?

    – n0rd
    Nov 17 '18 at 20:40












  • Thanks I just add them in.

    – dcpartners
    Nov 17 '18 at 20:42











  • Is it OK to call TryUpdateModelAsync twice? Why don't you just set the properties to their expected values, especially, since values are not coming from the request?

    – n0rd
    Nov 17 '18 at 21:05











  • @n0rd what do mean? How to pass the calculate value into this?

    – dcpartners
    Nov 17 '18 at 21:52











  • hostToUpdate.LastModified = DateTime.Now. Why do you need to pass anything anywhere?

    – n0rd
    Nov 17 '18 at 21:56


















  • Is this question missing an asp.net-core tag?

    – n0rd
    Nov 17 '18 at 20:40












  • Thanks I just add them in.

    – dcpartners
    Nov 17 '18 at 20:42











  • Is it OK to call TryUpdateModelAsync twice? Why don't you just set the properties to their expected values, especially, since values are not coming from the request?

    – n0rd
    Nov 17 '18 at 21:05











  • @n0rd what do mean? How to pass the calculate value into this?

    – dcpartners
    Nov 17 '18 at 21:52











  • hostToUpdate.LastModified = DateTime.Now. Why do you need to pass anything anywhere?

    – n0rd
    Nov 17 '18 at 21:56

















Is this question missing an asp.net-core tag?

– n0rd
Nov 17 '18 at 20:40






Is this question missing an asp.net-core tag?

– n0rd
Nov 17 '18 at 20:40














Thanks I just add them in.

– dcpartners
Nov 17 '18 at 20:42





Thanks I just add them in.

– dcpartners
Nov 17 '18 at 20:42













Is it OK to call TryUpdateModelAsync twice? Why don't you just set the properties to their expected values, especially, since values are not coming from the request?

– n0rd
Nov 17 '18 at 21:05





Is it OK to call TryUpdateModelAsync twice? Why don't you just set the properties to their expected values, especially, since values are not coming from the request?

– n0rd
Nov 17 '18 at 21:05













@n0rd what do mean? How to pass the calculate value into this?

– dcpartners
Nov 17 '18 at 21:52





@n0rd what do mean? How to pass the calculate value into this?

– dcpartners
Nov 17 '18 at 21:52













hostToUpdate.LastModified = DateTime.Now. Why do you need to pass anything anywhere?

– n0rd
Nov 17 '18 at 21:56






hostToUpdate.LastModified = DateTime.Now. Why do you need to pass anything anywhere?

– n0rd
Nov 17 '18 at 21:56













1 Answer
1






active

oldest

votes


















2





+100









You can set the (alternative) value prior to saving the object:



var hostToUpdate = await _context.Host.FindAsync(s => s.Id == id);

if (await TryUpdateModelAsync(
hostToUpdate,
"host", // empty with MVC
s => s.Name, s => s.Description, s => s.Address,
s => s.Postcode, s => s.Suburb, s => s.State))

try

hostToUpdate.LastModified = DateTime.Now;
hostToUpdate.LastDateModifiedBy = _userManager.GetUserId(User);

await _context.SaveChangesAsync();
return RedirectToPage("./Index");

// ...



Please note that LastModified and LastDateModifiedBy are not part of the TryUpdateModelAsync statement. But if they were, the values will be overwritten by the action.




From the Razor Pages documentation:




The DB context keeps track of whether entities in memory are in sync
with their corresponding rows in the DB. The DB context sync
information determines what happens when SaveChangesAsync is called.




From the Mvc documentation (no longer updated):




The Entity Framework's automatic change tracking sets the Modified
flag on the fields that are changed by form input. When the
SaveChanges method is called, the Entity Framework creates SQL
statements to update the database row.




To explain why this works, first TryUpdateModelAsync updates the fields updated by the user and then the action updates the additional fields. All are tracked and saved by the Entity Framework. This is default Entity Framework behaviour.




As a side note, an alternative for you would be to add code that automatically updates the fields. In that case you can't forget to set them and you save a few lines of code. And you won't have to change your code at all.



The strategy is that the entity implements the base fields and updates them on save changes. Here's a more extended version:



public interface IBaseEntity

DateTime LastDateModified get; set;
string LastDateModifiedBy get; set;

DateTime DateCreated get; set;
string DateCreatedBy get; set;


public class Host : IBaseEntity

// the other fields
// ...

public DateTime LastDateModified get; set;
public string LastDateModifiedBy get; set;

public DateTime DateCreated get; set;
public string DateCreatedBy get; set;



The context:



public partial class MyContext : DbContext

// Reference to the name of the current user.
private readonly string _userName;

public MyContext(DbContextOptions<MyContext> options, IHttpContextAccessor httpContext)
: base(options)

// Save the name of the current user so MyContext knows
// who it is. The advantage is that you won't need to lookup
// the User on each save changes.
_userName = httpContext.HttpContext.User.Identity.Name;


public virtual DbSet<Host> Host get; set;

// You'll only need to override this, unless you are
// also using non-async SaveChanges.
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))

UpdateEntries();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);


// You can move this to another partial class.
private void UpdateEntries()

// Modified
var modified = ChangeTracker.Entries().Where(v => v.State == EntityState.Modified && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
modified.ForEach(entry =>

((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastDateModifiedBy = _userName;
);

// Added
var added = ChangeTracker.Entries().Where(v => v.State == EntityState.Added && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
added.ForEach(entry =>

((IBaseEntity)entry.Entity).DateCreated = DateTime.UtcNow;
((IBaseEntity)entry.Entity).DateCreatedBy = _userName;
((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastDateModifiedBy = _userName;
);


// ...



Add HttpContextAccessor in Startup:



public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();


Now each time an object that implements IBaseEntity is saved, the fields are automatically updated.



Please note that I didn't inject UserManager here. If the User contains a name claim then you can use that instead. This will save a call to the database.



As an improvement you can write a new service that takes care of resolving the user name and inject that instead.






share|improve this answer

























  • Thanks for the side note ... we like that solution :)

    – dcpartners
    Nov 19 '18 at 3:01











  • how to inject the UserManager into this ApplicationDbContext() stuff to get ID of the user?

    – dcpartners
    Nov 23 '18 at 0:32











  • Replace IHttpContextAccessor httpContext with UserManager<ApplicationUser> userManager.

    – Ruard van Elburg
    Nov 23 '18 at 8:16











Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53293634%2fhow-to-add-calculated-value-into-this-tryupdatemodelasync%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









2





+100









You can set the (alternative) value prior to saving the object:



var hostToUpdate = await _context.Host.FindAsync(s => s.Id == id);

if (await TryUpdateModelAsync(
hostToUpdate,
"host", // empty with MVC
s => s.Name, s => s.Description, s => s.Address,
s => s.Postcode, s => s.Suburb, s => s.State))

try

hostToUpdate.LastModified = DateTime.Now;
hostToUpdate.LastDateModifiedBy = _userManager.GetUserId(User);

await _context.SaveChangesAsync();
return RedirectToPage("./Index");

// ...



Please note that LastModified and LastDateModifiedBy are not part of the TryUpdateModelAsync statement. But if they were, the values will be overwritten by the action.




From the Razor Pages documentation:




The DB context keeps track of whether entities in memory are in sync
with their corresponding rows in the DB. The DB context sync
information determines what happens when SaveChangesAsync is called.




From the Mvc documentation (no longer updated):




The Entity Framework's automatic change tracking sets the Modified
flag on the fields that are changed by form input. When the
SaveChanges method is called, the Entity Framework creates SQL
statements to update the database row.




To explain why this works, first TryUpdateModelAsync updates the fields updated by the user and then the action updates the additional fields. All are tracked and saved by the Entity Framework. This is default Entity Framework behaviour.




As a side note, an alternative for you would be to add code that automatically updates the fields. In that case you can't forget to set them and you save a few lines of code. And you won't have to change your code at all.



The strategy is that the entity implements the base fields and updates them on save changes. Here's a more extended version:



public interface IBaseEntity

DateTime LastDateModified get; set;
string LastDateModifiedBy get; set;

DateTime DateCreated get; set;
string DateCreatedBy get; set;


public class Host : IBaseEntity

// the other fields
// ...

public DateTime LastDateModified get; set;
public string LastDateModifiedBy get; set;

public DateTime DateCreated get; set;
public string DateCreatedBy get; set;



The context:



public partial class MyContext : DbContext

// Reference to the name of the current user.
private readonly string _userName;

public MyContext(DbContextOptions<MyContext> options, IHttpContextAccessor httpContext)
: base(options)

// Save the name of the current user so MyContext knows
// who it is. The advantage is that you won't need to lookup
// the User on each save changes.
_userName = httpContext.HttpContext.User.Identity.Name;


public virtual DbSet<Host> Host get; set;

// You'll only need to override this, unless you are
// also using non-async SaveChanges.
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))

UpdateEntries();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);


// You can move this to another partial class.
private void UpdateEntries()

// Modified
var modified = ChangeTracker.Entries().Where(v => v.State == EntityState.Modified && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
modified.ForEach(entry =>

((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastDateModifiedBy = _userName;
);

// Added
var added = ChangeTracker.Entries().Where(v => v.State == EntityState.Added && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
added.ForEach(entry =>

((IBaseEntity)entry.Entity).DateCreated = DateTime.UtcNow;
((IBaseEntity)entry.Entity).DateCreatedBy = _userName;
((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastDateModifiedBy = _userName;
);


// ...



Add HttpContextAccessor in Startup:



public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();


Now each time an object that implements IBaseEntity is saved, the fields are automatically updated.



Please note that I didn't inject UserManager here. If the User contains a name claim then you can use that instead. This will save a call to the database.



As an improvement you can write a new service that takes care of resolving the user name and inject that instead.






share|improve this answer

























  • Thanks for the side note ... we like that solution :)

    – dcpartners
    Nov 19 '18 at 3:01











  • how to inject the UserManager into this ApplicationDbContext() stuff to get ID of the user?

    – dcpartners
    Nov 23 '18 at 0:32











  • Replace IHttpContextAccessor httpContext with UserManager<ApplicationUser> userManager.

    – Ruard van Elburg
    Nov 23 '18 at 8:16
















2





+100









You can set the (alternative) value prior to saving the object:



var hostToUpdate = await _context.Host.FindAsync(s => s.Id == id);

if (await TryUpdateModelAsync(
hostToUpdate,
"host", // empty with MVC
s => s.Name, s => s.Description, s => s.Address,
s => s.Postcode, s => s.Suburb, s => s.State))

try

hostToUpdate.LastModified = DateTime.Now;
hostToUpdate.LastDateModifiedBy = _userManager.GetUserId(User);

await _context.SaveChangesAsync();
return RedirectToPage("./Index");

// ...



Please note that LastModified and LastDateModifiedBy are not part of the TryUpdateModelAsync statement. But if they were, the values will be overwritten by the action.




From the Razor Pages documentation:




The DB context keeps track of whether entities in memory are in sync
with their corresponding rows in the DB. The DB context sync
information determines what happens when SaveChangesAsync is called.




From the Mvc documentation (no longer updated):




The Entity Framework's automatic change tracking sets the Modified
flag on the fields that are changed by form input. When the
SaveChanges method is called, the Entity Framework creates SQL
statements to update the database row.




To explain why this works, first TryUpdateModelAsync updates the fields updated by the user and then the action updates the additional fields. All are tracked and saved by the Entity Framework. This is default Entity Framework behaviour.




As a side note, an alternative for you would be to add code that automatically updates the fields. In that case you can't forget to set them and you save a few lines of code. And you won't have to change your code at all.



The strategy is that the entity implements the base fields and updates them on save changes. Here's a more extended version:



public interface IBaseEntity

DateTime LastDateModified get; set;
string LastDateModifiedBy get; set;

DateTime DateCreated get; set;
string DateCreatedBy get; set;


public class Host : IBaseEntity

// the other fields
// ...

public DateTime LastDateModified get; set;
public string LastDateModifiedBy get; set;

public DateTime DateCreated get; set;
public string DateCreatedBy get; set;



The context:



public partial class MyContext : DbContext

// Reference to the name of the current user.
private readonly string _userName;

public MyContext(DbContextOptions<MyContext> options, IHttpContextAccessor httpContext)
: base(options)

// Save the name of the current user so MyContext knows
// who it is. The advantage is that you won't need to lookup
// the User on each save changes.
_userName = httpContext.HttpContext.User.Identity.Name;


public virtual DbSet<Host> Host get; set;

// You'll only need to override this, unless you are
// also using non-async SaveChanges.
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))

UpdateEntries();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);


// You can move this to another partial class.
private void UpdateEntries()

// Modified
var modified = ChangeTracker.Entries().Where(v => v.State == EntityState.Modified && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
modified.ForEach(entry =>

((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastDateModifiedBy = _userName;
);

// Added
var added = ChangeTracker.Entries().Where(v => v.State == EntityState.Added && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
added.ForEach(entry =>

((IBaseEntity)entry.Entity).DateCreated = DateTime.UtcNow;
((IBaseEntity)entry.Entity).DateCreatedBy = _userName;
((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastDateModifiedBy = _userName;
);


// ...



Add HttpContextAccessor in Startup:



public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();


Now each time an object that implements IBaseEntity is saved, the fields are automatically updated.



Please note that I didn't inject UserManager here. If the User contains a name claim then you can use that instead. This will save a call to the database.



As an improvement you can write a new service that takes care of resolving the user name and inject that instead.






share|improve this answer

























  • Thanks for the side note ... we like that solution :)

    – dcpartners
    Nov 19 '18 at 3:01











  • how to inject the UserManager into this ApplicationDbContext() stuff to get ID of the user?

    – dcpartners
    Nov 23 '18 at 0:32











  • Replace IHttpContextAccessor httpContext with UserManager<ApplicationUser> userManager.

    – Ruard van Elburg
    Nov 23 '18 at 8:16














2





+100







2





+100



2




+100





You can set the (alternative) value prior to saving the object:



var hostToUpdate = await _context.Host.FindAsync(s => s.Id == id);

if (await TryUpdateModelAsync(
hostToUpdate,
"host", // empty with MVC
s => s.Name, s => s.Description, s => s.Address,
s => s.Postcode, s => s.Suburb, s => s.State))

try

hostToUpdate.LastModified = DateTime.Now;
hostToUpdate.LastDateModifiedBy = _userManager.GetUserId(User);

await _context.SaveChangesAsync();
return RedirectToPage("./Index");

// ...



Please note that LastModified and LastDateModifiedBy are not part of the TryUpdateModelAsync statement. But if they were, the values will be overwritten by the action.




From the Razor Pages documentation:




The DB context keeps track of whether entities in memory are in sync
with their corresponding rows in the DB. The DB context sync
information determines what happens when SaveChangesAsync is called.




From the Mvc documentation (no longer updated):




The Entity Framework's automatic change tracking sets the Modified
flag on the fields that are changed by form input. When the
SaveChanges method is called, the Entity Framework creates SQL
statements to update the database row.




To explain why this works, first TryUpdateModelAsync updates the fields updated by the user and then the action updates the additional fields. All are tracked and saved by the Entity Framework. This is default Entity Framework behaviour.




As a side note, an alternative for you would be to add code that automatically updates the fields. In that case you can't forget to set them and you save a few lines of code. And you won't have to change your code at all.



The strategy is that the entity implements the base fields and updates them on save changes. Here's a more extended version:



public interface IBaseEntity

DateTime LastDateModified get; set;
string LastDateModifiedBy get; set;

DateTime DateCreated get; set;
string DateCreatedBy get; set;


public class Host : IBaseEntity

// the other fields
// ...

public DateTime LastDateModified get; set;
public string LastDateModifiedBy get; set;

public DateTime DateCreated get; set;
public string DateCreatedBy get; set;



The context:



public partial class MyContext : DbContext

// Reference to the name of the current user.
private readonly string _userName;

public MyContext(DbContextOptions<MyContext> options, IHttpContextAccessor httpContext)
: base(options)

// Save the name of the current user so MyContext knows
// who it is. The advantage is that you won't need to lookup
// the User on each save changes.
_userName = httpContext.HttpContext.User.Identity.Name;


public virtual DbSet<Host> Host get; set;

// You'll only need to override this, unless you are
// also using non-async SaveChanges.
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))

UpdateEntries();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);


// You can move this to another partial class.
private void UpdateEntries()

// Modified
var modified = ChangeTracker.Entries().Where(v => v.State == EntityState.Modified && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
modified.ForEach(entry =>

((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastDateModifiedBy = _userName;
);

// Added
var added = ChangeTracker.Entries().Where(v => v.State == EntityState.Added && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
added.ForEach(entry =>

((IBaseEntity)entry.Entity).DateCreated = DateTime.UtcNow;
((IBaseEntity)entry.Entity).DateCreatedBy = _userName;
((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastDateModifiedBy = _userName;
);


// ...



Add HttpContextAccessor in Startup:



public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();


Now each time an object that implements IBaseEntity is saved, the fields are automatically updated.



Please note that I didn't inject UserManager here. If the User contains a name claim then you can use that instead. This will save a call to the database.



As an improvement you can write a new service that takes care of resolving the user name and inject that instead.






share|improve this answer















You can set the (alternative) value prior to saving the object:



var hostToUpdate = await _context.Host.FindAsync(s => s.Id == id);

if (await TryUpdateModelAsync(
hostToUpdate,
"host", // empty with MVC
s => s.Name, s => s.Description, s => s.Address,
s => s.Postcode, s => s.Suburb, s => s.State))

try

hostToUpdate.LastModified = DateTime.Now;
hostToUpdate.LastDateModifiedBy = _userManager.GetUserId(User);

await _context.SaveChangesAsync();
return RedirectToPage("./Index");

// ...



Please note that LastModified and LastDateModifiedBy are not part of the TryUpdateModelAsync statement. But if they were, the values will be overwritten by the action.




From the Razor Pages documentation:




The DB context keeps track of whether entities in memory are in sync
with their corresponding rows in the DB. The DB context sync
information determines what happens when SaveChangesAsync is called.




From the Mvc documentation (no longer updated):




The Entity Framework's automatic change tracking sets the Modified
flag on the fields that are changed by form input. When the
SaveChanges method is called, the Entity Framework creates SQL
statements to update the database row.




To explain why this works, first TryUpdateModelAsync updates the fields updated by the user and then the action updates the additional fields. All are tracked and saved by the Entity Framework. This is default Entity Framework behaviour.




As a side note, an alternative for you would be to add code that automatically updates the fields. In that case you can't forget to set them and you save a few lines of code. And you won't have to change your code at all.



The strategy is that the entity implements the base fields and updates them on save changes. Here's a more extended version:



public interface IBaseEntity

DateTime LastDateModified get; set;
string LastDateModifiedBy get; set;

DateTime DateCreated get; set;
string DateCreatedBy get; set;


public class Host : IBaseEntity

// the other fields
// ...

public DateTime LastDateModified get; set;
public string LastDateModifiedBy get; set;

public DateTime DateCreated get; set;
public string DateCreatedBy get; set;



The context:



public partial class MyContext : DbContext

// Reference to the name of the current user.
private readonly string _userName;

public MyContext(DbContextOptions<MyContext> options, IHttpContextAccessor httpContext)
: base(options)

// Save the name of the current user so MyContext knows
// who it is. The advantage is that you won't need to lookup
// the User on each save changes.
_userName = httpContext.HttpContext.User.Identity.Name;


public virtual DbSet<Host> Host get; set;

// You'll only need to override this, unless you are
// also using non-async SaveChanges.
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))

UpdateEntries();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);


// You can move this to another partial class.
private void UpdateEntries()

// Modified
var modified = ChangeTracker.Entries().Where(v => v.State == EntityState.Modified && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
modified.ForEach(entry =>

((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastDateModifiedBy = _userName;
);

// Added
var added = ChangeTracker.Entries().Where(v => v.State == EntityState.Added && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
added.ForEach(entry =>

((IBaseEntity)entry.Entity).DateCreated = DateTime.UtcNow;
((IBaseEntity)entry.Entity).DateCreatedBy = _userName;
((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastDateModifiedBy = _userName;
);


// ...



Add HttpContextAccessor in Startup:



public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();


Now each time an object that implements IBaseEntity is saved, the fields are automatically updated.



Please note that I didn't inject UserManager here. If the User contains a name claim then you can use that instead. This will save a call to the database.



As an improvement you can write a new service that takes care of resolving the user name and inject that instead.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 18 '18 at 19:07

























answered Nov 18 '18 at 13:38









Ruard van ElburgRuard van Elburg

5,99121129




5,99121129












  • Thanks for the side note ... we like that solution :)

    – dcpartners
    Nov 19 '18 at 3:01











  • how to inject the UserManager into this ApplicationDbContext() stuff to get ID of the user?

    – dcpartners
    Nov 23 '18 at 0:32











  • Replace IHttpContextAccessor httpContext with UserManager<ApplicationUser> userManager.

    – Ruard van Elburg
    Nov 23 '18 at 8:16


















  • Thanks for the side note ... we like that solution :)

    – dcpartners
    Nov 19 '18 at 3:01











  • how to inject the UserManager into this ApplicationDbContext() stuff to get ID of the user?

    – dcpartners
    Nov 23 '18 at 0:32











  • Replace IHttpContextAccessor httpContext with UserManager<ApplicationUser> userManager.

    – Ruard van Elburg
    Nov 23 '18 at 8:16

















Thanks for the side note ... we like that solution :)

– dcpartners
Nov 19 '18 at 3:01





Thanks for the side note ... we like that solution :)

– dcpartners
Nov 19 '18 at 3:01













how to inject the UserManager into this ApplicationDbContext() stuff to get ID of the user?

– dcpartners
Nov 23 '18 at 0:32





how to inject the UserManager into this ApplicationDbContext() stuff to get ID of the user?

– dcpartners
Nov 23 '18 at 0:32













Replace IHttpContextAccessor httpContext with UserManager<ApplicationUser> userManager.

– Ruard van Elburg
Nov 23 '18 at 8:16






Replace IHttpContextAccessor httpContext with UserManager<ApplicationUser> userManager.

– Ruard van Elburg
Nov 23 '18 at 8:16




















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53293634%2fhow-to-add-calculated-value-into-this-tryupdatemodelasync%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Kleinkühnau

Makov (Slowakei)

Deutsches Schauspielhaus