如何将计算的值添加到此TryUpdateModelAsync <>

时间:2018-11-14 05:20:13

标签: c# asp.net-core .net-core

我有以下代码:

ICommand _navigateAddNewAddress;
    public ICommand NavigateAddNewAddress => _navigateAddNewAddress ?? (_navigateAddNewAddress = new Command(async (sender, args) =>
    {
        try
        {
            App.PushPage(AppPage.PROFILE_ADD_NEW_RESIDENTIAL_ADDRESS);
        }
        catch (Exception ex)
        {
            App.LogError(ex);
        }
    }));

我为Host设置了一个属性: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"); } } LastDateModified,该属性是计算得出/预定义的值

即{{1}的LastModifiedDateTime.Now的{​​{1}}。

那么如何将其传递给此代码?

LastDateModified

1 个答案:

答案 0 :(得分:2)

您可以在保存对象之前设置(替代)值:

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");
    }
    // ...
}

请注意,LastModifiedLastDateModifiedBy不是TryUpdateModelAsync语句的一部分。但是如果是的话,这些值将被操作覆盖。


从剃须刀页面documentation

  

DB上下文跟踪内存中的实体是否同步   以及它们在数据库中的对应行。数据库上下文同步   信息确定调用SaveChangesAsync时会发生什么。

通过Mvc documentation(不再更新):

  

实体框架的自动更改跟踪设置了已修改   在由表单输入更改的字段上标记。当。。。的时候   调用SaveChanges方法后,实体框架创建SQL   语句以更新数据库行。

要解释为什么这样做,首先TryUpdateModelAsync更新用户更新的字段,然后该操作更新其他字段。所有这些都由实体框架进行跟踪和保存。这是默认的实体框架行为。


请注意,您可以选择添加自动更新字段的代码。在那种情况下,您不会忘记设置它们,并且节省了几行代码。而且您根本不必更改代码。

该策略是实体实现基本字段并在保存更改时对其进行更新。这是更扩展的版本:

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; }
}

上下文:

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;
        });
    }

    // ...
}

在启动中添加HttpContextAccessor:

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

现在,每次保存实现IBaseEntity的对象时,这些字段都会自动更新。

请注意,我没有在此处注入UserManager。如果用户包含名称声明,则可以使用该名称声明。这将保存对数据库的调用。

作为一项改进,您可以编写一个新服务来解决用户名并注入用户名。