#c# #asp.net #asp.net-core-3.1
#c# #asp.net #asp.net-core-3.1
Вопрос:
Я создаю REST API в NET Core 3.1. У меня есть таблица с именем GrRefBancos
со столбцами для аудита того, кто и когда вставляет или обновляет регистры. Как я могу заполнить эти столбцы автоматически без необходимости отправлять значения при выполнении вставки или обновления?
Это мой код:
public abstract class BaseEntity
{
public long Id { get; set; }
public DateTime CreadoEl { get; set; }
public string CreadoPor { get; set; }
public DateTime? ActualizaEl { get; set; }
public string ActualizaPor { get; set; }
public DateTime? InactivaEl { get; set; }
}
public partial class GrRefBancos : BaseEntity
{
public GrRefBancos()
{
IcFacturas = new HashSet<IcFacturas>();
PrFacturas = new HashSet<PrFacturas>();
}
public string CodigoBanco { get; set; }
public string Descripcion { get; set; }
public virtual ICollection<IcFacturas> IcFacturas { get; set; }
public virtual ICollection<PrFacturas> PrFacturas { get; set; }
}
В DbContext
я попытался с HasDefaultValue()
помощью функции вставить, но это не сработало:
modelBuilder.Entity<GrRefBancos>(entity =>
{
entity.HasKey(e => e.Id)
.HasName("PK_grrefbancos")
.IsClustered(false);
entity.Property(e => e.ActualizaEl).HasColumnType("datetime");
entity.Property(e => e.ActualizaPor)
.HasMaxLength(50)
.IsUnicode(false);
entity.Property(e => e.CodigoBanco)
.IsRequired()
.HasMaxLength(10)
.IsUnicode(false);
entity.Property(e => e.CreadoEl)
.HasColumnType("datetime")
.HasDefaultValue(DateTime.Now);
entity.Property(e => e.CreadoPor)
.IsRequired()
.HasMaxLength(50)
.HasDefaultValue("LoogedUser")
.IsUnicode(false);
entity.Property(e => e.Descripcion)
.HasMaxLength(100)
.IsUnicode(false);
entity.Property(e => e.InactivaEl)
.HasColumnName("InactivaEL")
.HasColumnType("datetime");
});
Спасибо за ваше внимание.
Ответ №1:
Вам нужно будет переопределить SaveChanges()
или SaveChangesAsync()
. Затем вам нужно будет захватить ChangeTracker
и запросить у него все записи объекта с помощью changeTracker.Entries()
.
Выполните итерацию по возвращенной коллекции, проверьте Entry.State
, является ли это вставкой или обновлением, и соответствующим образом измените столбцы аудита.
Вам DbContext
необходимо будет знать о пользователе, который выполняет операцию, однако об этом легко позаботиться благодаря разумному использованию DI. Если DI невозможен из-за циклических зависимостей, затем вызовите событие из SaveChanges[Async]()
переопределения выше и запросите имя пользователя.
PS: Вы можете решить не делать их частью своей модели, сделав столбцы аудита теневыми для свойств EF.
Комментарии:
1. Это решение работает для меня, я использовал ChangeTracker в dbcontext и переопределил функцию SaveChanges для обновления полей временных меток. Спасибо за ваш совет.
Ответ №2:
С помощью триггера в таблицах. И определять их как вычисленные в метамодели EFCore, чтобы Ef не пытался их обновлять.
Однако это не сработает, потому что при нормальной современной настройке с объединением в пул соединений база данных понятия не имеет, кто на самом деле является пользователем. Дата / временная метка «простая» — имя пользователя просто недоступно в серверной части.
Комментарии:
1. Спасибо за ответ. Вы правы, по триггеру не работает, потому что база данных не знает реального пользователя из приложения. Мне нужно сделать это с помощью кода. Я новичок в Net Core, и я искал решение, но в документе, который я прочитал, говорится, что для вставки определите значение по умолчанию с HasDefaultValue, но это тоже не работает
2. Нет. Вам нужно запрограммировать его в методы, в которых вы выполняете обновление. Или в SaveChanges, просматривая все изменения.
Ответ №3:
Вы можете реализовать эту функцию в своем классе BaseRepository
class BaseRepository<T>{
private readonly DbContext _dbContext;
public BaseRepository(DbContext dbContext){
_dbContext = dbContext;
}
public Update(T Entity, UserEntity userEntity){
//Do anything you like here
}
public Insert(T Entity, UserEntity userEntity){
//Do anything you like here
}
}
и использовать его следующим образом
_unitOfWork.GetRepository<MyRepository>().Update(MyEntity,UserEntity);
//or
_unitOfWork.GetRepository<MyRepository>().Insert(MyEntity,UserEntity);
Вы можете реализовать свои сервисы, чтобы сделать это автоматически, возможно, передать HttpContext.User!
_myEntityService.Update(myDto,HttpContext.User);
//or
_myEntityService.Insert(myDto,HttpContext.User);
Это вызывающе возможно, и техническая реализация полностью зависит от того, чего вы действительно пытаетесь достичь.
Комментарии:
1. Сверните место (есть переопределение) И — предполагая, что кто-то использует шаблон репозитория, который является проверенным антишаблоном и который мне еще предстоит увидеть ДОЛЖНЫМ ОБРАЗОМ реализованным через 20 лет.
2. Спасибо, что поделились своим мнением @TomTom, вы абсолютно правы, однако я считаю, что общие репозитории полностью подходят для целей crud. Пример довольно упрощен, но не могли бы вы рассказать мне больше о проблеме реализации, о которой вы говорите. Я новичок, и я могу воспользоваться вашей помощью, чтобы отшлифовать код здесь.
3. Это бесполезный мертвый код для простых вещей (т. Е. Вы ничего не получаете — у вас ЕСТЬ репозиторий и единица работы, они называются DbContext. Как только вы попадаете в большее количество репозиториев compelx sceanrios, они полностью неисправны в 99,999% реализаций. Как и ваше обновление — я должен обновить только ОДНУ вещь, но как насчет элементов, на которые ссылаются? Как насчет загрузки — репозиторий не может должным образом обрабатывать включение без использования других репозиториев, о чем разработчики репозиториев, похоже, ВСЕГДА забывают. В конце концов, это тонна кода, без которого вы можете обойтись с точно такой же функциональностью. Добавление бесполезного кода = плохо.