Как добавить вычисленное значение в этот TryUpdateModelAsync <> - PullRequest
0 голосов
/ 14 ноября 2018

У меня есть следующий код:

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

У меня есть свойства для Host, называемые: LastDateModified и LastModified, которые вычисляются / предопределенные значения

т.е. DateTime.Now для LastDateModified и _userManager.GetUserId(User) для LastDateModifiedBy.

Так как мне передать это в этот код?

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

1 Ответ

0 голосов
/ 18 ноября 2018

Вы можете установить (альтернативное) значение перед сохранением объекта:

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

Обратите внимание, что LastModified и LastDateModifiedBy не являются частью оператора TryUpdateModelAsync. Но если они были, значения будут перезаписаны действием.


Из страниц бритвы Документация :

Контекст БД отслеживает, синхронизируются ли объекты в памяти с соответствующими им строками в БД. Синхронизация контекста БД информация определяет, что происходит при вызове SaveChangesAsync.

Из документации Mvc (больше не обновляется):

Автоматическое отслеживание изменений Entity Framework устанавливает Модифицированный помечать поля, которые изменяются при вводе формы. Когда Метод SaveChanges вызывается, Entity Framework создает SQL операторы для обновления строки базы данных.

Чтобы объяснить, почему это работает, сначала TryUpdateModelAsync обновляет поля, обновленные пользователем, а затем действие обновляет дополнительные поля. Все отслеживаются и сохраняются Entity Framework. Это поведение Entity Framework по умолчанию.


В качестве дополнительного примечания, альтернативой для вас будет добавить код, который автоматически обновляет поля . В этом случае вы не можете забыть установить их и сохранить несколько строк кода. И вам вообще не придется менять свой код.

Стратегия заключается в том, что сущность реализует базовые поля и обновляет их при сохранении изменений. Вот более расширенная версия:

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 здесь. Если пользователь содержит заявку на имя, вы можете использовать ее вместо. Это сохранит вызов в базе данных.

В качестве улучшения вы можете написать новый сервис, который позаботится о разрешении имени пользователя, и вместо этого ввести его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...