我有一个控制器,可以修改日历中的约会。我想使用SignalR集线器通知用户“用户X已更改{appointmentTitle}:列表:{属性} {OriginalValue} {NewValue}”
我是C#的初学者(语法上还可以,但是OOP概念是新的);我正在尝试使用事件来实现上述目标。 下面是处理程序和参数,控制器的摘录以及我的问题的摘要。
代码缩写!
public class AppointmentChangeEventArgs : EventArgs
{
public EntityState AppointmentState = EntityState.Unchanged;
public EntityEntry Entity = null;
public ScheduleData Appointment = null;
}
// maybe this could be just one, and let the consumer decide based on EntityState?
public EventHandler<AppointmentChangeEventArgs> AppointmentChanged;
public EventHandler<AppointmentChangeEventArgs> AppointmentAdded;
public EventHandler<AppointmentChangeEventArgs> AppointmentRemoved;
protected virtual void OnAppointment(AppointmentChangeEventArgs appointmentChangeEventArgs)
{
switch (appointmentChangeEventArgs.AppointmentState)
{
case EntityState.Added:
AppointmentAdded?.Invoke(this, appointmentChangeEventArgs);
break;
case EntityState.Deleted:
AppointmentRemoved?.Invoke(this, appointmentChangeEventArgs);
break;
case EntityState.Modified:
AppointmentChanged?.Invoke(this, appointmentChangeEventArgs);
break;
default:
break;
}
}
public async Task<IActionResult> Batch([FromBody] ScheduleEditParameters param)
switch (param.Action) {
case "insert":
await _dbContext.Appointments.AddAsync(appointment);
break;
case "update":
// .. get Appointment from DB
appointment.Subject = value.Subject;
appointment.StartTime = value.StartTime;
// ...
case "remove":
// .. get Appointment from DB
_dbContext.Appointments.Remove(appointment);
}
var modifiedEntries = _dbContext.ChangeTracker
.Entries()
.Where(x => x.State != EntityState.Unchanged && x.State != EntityState.Detached)
.Select(x => new AppointmentChangeEventArgs() { Entity = (EntityEntry) x.Entity, AppointmentState = x.State, Appointment = appointment })
.ToList();
if (modifiedEntries.Any())
{
var notificationService = new NotificationService(signalRHub, notificationLogger);
AppointmentAdded += notificationService.OnAppointmentChanged;
AppointmentChanged += notificationService.OnAppointmentChanged;
AppointmentRemoved += notificationService.OnAppointmentChanged;
}
await _dbContext.SaveChangesAsync();
_dbContext.Entry(modifiedEntry).Properties.Where(x => x.IsModified).ToList();
-但这属于NotificationService类吗?为此,我还需要将DbContext传递给NotificationService。如果您能提供有关如何组织和处理此任务的见解,我将不胜感激。谢谢。
答案 0 :(得分:1)
首先,我通常建议您不在此处使用事件。事件听起来可能很有用,但是由于它们(同步)的工作方式,它们并不是在Web上下文中实现此目的的最佳方法,尤其是在像ASP.NET Core这样的异步框架中。
相反,我建议您仅声明自己的类型,例如IAppointmentChangeHandler
像这样:
public interface IAppointmentChangeHandler
{
Task AddAppointment(ScheduleData appointment);
Task UpdateAppointment(ScheduleData appointment);
Task RemoveAppointment(ScheduleData appointment);
}
您的NotificationService
可以实现该接口以处理那些事件(显然,只要发送您需要发送的内容即可)
public class NotificationService : IAppointmentChangeHandler
{
private readonly IHubContext _hubContext;
public NotificationService(IHubContext hubContext)
{
_hubContext = hubContext;
}
public AddAppointment(ScheduleData appointment)
{
await _hubContext.Clients.InvokeAsync("AddAppointment", appointment);
}
public UpdateAppointment(ScheduleData appointment)
{
await _hubContext.Clients.InvokeAsync("UpdateAppointment", appointment);
}
public RemoveAppointment(ScheduleData appointment)
{
await _hubContext.Clients.InvokeAsync("RemoveAppointment", appointment);
}
}
在控制器内部,您只需注入IAppointmentChangeHandler
,然后在其上调用实际方法。这样,您就可以将控制器和通知服务完全解耦:控制器不需要先 construct 类型,并且您也不需要预订某些事件(也必须取消订阅)从某个时候再次顺便说一句)。您可以将实例完全留给DI容器。
要回答您的个人问题:
可以在事件参数中使用EntityEntry和EntityState吗?
我会避免在数据库外部的上下文中使用它。两者都是数据库设置的实现细节,因为您在这里使用实体框架。这不仅会使您的事件处理程序与Entity Framework紧密地结合在一起(这意味着每个想要成为事件处理程序的人都需要引用EF,即使他们没有对它做任何事情),而且您还在泄漏可能改变的内部状态稍后(您不拥有EntityEntry
的{{1}},那么谁知道之后EF会做什么)。
对于每个修改后的条目,我可以获得
_dbContext.Entry(modifiedEntry).Properties.Where(x => x.IsModified).ToList();
如果您查看代码,则首先要在数据库集上调用Add
,Update
或Remove
;然后您使用某种逻辑来查看某些内部 EF东西,以找出确实完全相同的东西。如果直接在这三个AppointmentChangeEventArgs
情况下构造switch
,则可以使事情复杂度降低。
但这是否属于NotificationService类?为此,我还需要将DbContext传递给NotificationService。
通知服务与数据库有关吗?我会说不。除非您将这些通知持久保存到数据库中。当我想到通知服务时,我希望能够在其上调用某些东西来主动触发通知,而不是在服务中使用某种逻辑来弄清楚它可能触发哪些通知。
有没有更简单的方法可以实现这一目标?添加和删除处理程序很容易(“用户X已添加|删除...约会{Title}”),但是要弄清楚确切的变化,我必须查看修改后的属性。
首先以最简单的方式考虑它:在哪里更新数据库实体的值?在update
情况下。因此,从复制的对象中复制值时,也可以只检查实际更改的属性。这样,您就可以轻松记录需要通知的属性。
将其与EF完全分离,从长远来看,您将变得更加灵活。