Writing an event based SignalR Notification Service using DBContext ChangeTracker - separation of concernsSeparation of concerns and n-tiered architecture in ASP.NET 5/ASP.NET Core 1EntityFrameworkCore quite a dbcontext or should Separating DbContext In .Net Core Like Bounded Context For Getting PerformanceAsp.net core separation of concern using service layerASP.NET Core event based background tasks with hosted servicesAzure SignalR Service Error - Message rate reaches limitHow to access SignalR connection from Azure Service Bus event handlerAzure SignalR Service with native Android app?Resolve scoped service from pooled DbContext in EF CoreIs it possible to write the contents of a Sqlite DbContext into a different file
Does an object count as "being moved" when placed in a Bag of Holding before its wielder moves, and then after moving they take the object out again?
Why can't an Airbus A330 dump fuel in an emergency?
Is refusing to concede in the face of an unstoppable Nexus combo punishable?
Three Singles in Three Clubs
Is there such a thing as too inconvenient?
How to refer to a regex group in awk regex?
If all stars rotate, why was there a theory developed, that requires non-rotating stars?
How to persuade recruiters to send me the Job Description?
Which household object drew this pattern?
Check in to 2 hotels at same location
Why were movies shot on film shot at 24 frames per second?
Is “I am getting married with my sister” ambiguous?
Script that helps people make better choices
What does it mean to have a subnet mask /32?
Can you help me understand Modes from the aspect of chord changes?
Do AT motherboards (286, 386, 486) really need -5V (besides redirecting it to ISA connectors)?
Why is 日本 read as "nihon" but not "nitsuhon"?
How is "sein" conjugated in this sub-sentence?
Was Tuvok bluffing when he said that Voyager's transporters rendered the Kazon weapons useless?
How should I face my manager if I make a mistake because a senior coworker explained something incorrectly to me?
What is the hex versus octal timeline?
Is it insecure to have an ansible user with passwordless sudo?
Shouldn't the "credit score" prevent Americans from going deeper and deeper into personal debt?
Why is Boris Johnson visiting only Paris & Berlin if every member of the EU needs to agree on a withdrawal deal?
Writing an event based SignalR Notification Service using DBContext ChangeTracker - separation of concerns
Separation of concerns and n-tiered architecture in ASP.NET 5/ASP.NET Core 1EntityFrameworkCore quite a dbcontext or should Separating DbContext In .Net Core Like Bounded Context For Getting PerformanceAsp.net core separation of concern using service layerASP.NET Core event based background tasks with hosted servicesAzure SignalR Service Error - Message rate reaches limitHow to access SignalR connection from Azure Service Bus event handlerAzure SignalR Service with native Android app?Resolve scoped service from pooled DbContext in EF CoreIs it possible to write the contents of a Sqlite DbContext into a different file
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
I have a controller that modifies appointments in a calendar. I want to use my SignalR hub to notify users à la "User X changed appointmentTitle: List: Property OriginalValue NewValue"
I'm a beginner in C# (Syntax-wise it's ok, but OOP concepts are new); I'm trying to use events to achieve the above.
Below are the handlers and arguments, an extract from the controller and a summary of my questions.
Code is abbreviated!
EventArgs
public class AppointmentChangeEventArgs : EventArgs
public EntityState AppointmentState = EntityState.Unchanged;
public EntityEntry Entity = null;
public ScheduleData Appointment = null;
EventHandler
// 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;
Controller
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();
Questions
- Is it ok to use EntityEntry and EntityState in event arguments?
- for each modified Entry, I can obtain
_dbContext.Entry(modifiedEntry).Properties.Where(x => x.IsModified).ToList();
- but does this belong in the NotificationService class? In order to do that, I'd also need to pass the DbContext over to NotificationService. - Might there be a simpler way to achieve this? Adding and Removing handlers are easy ("User X has added|removed ... appointment Title"), but in order to figure out the exact changes I'll have to look at the modified properties.
I'd be grateful if you could provide an insight into how you would structure & handle this task. Thank you.
asp.net-core entity-framework-core asp.net-core-signalr
add a comment |
I have a controller that modifies appointments in a calendar. I want to use my SignalR hub to notify users à la "User X changed appointmentTitle: List: Property OriginalValue NewValue"
I'm a beginner in C# (Syntax-wise it's ok, but OOP concepts are new); I'm trying to use events to achieve the above.
Below are the handlers and arguments, an extract from the controller and a summary of my questions.
Code is abbreviated!
EventArgs
public class AppointmentChangeEventArgs : EventArgs
public EntityState AppointmentState = EntityState.Unchanged;
public EntityEntry Entity = null;
public ScheduleData Appointment = null;
EventHandler
// 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;
Controller
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();
Questions
- Is it ok to use EntityEntry and EntityState in event arguments?
- for each modified Entry, I can obtain
_dbContext.Entry(modifiedEntry).Properties.Where(x => x.IsModified).ToList();
- but does this belong in the NotificationService class? In order to do that, I'd also need to pass the DbContext over to NotificationService. - Might there be a simpler way to achieve this? Adding and Removing handlers are easy ("User X has added|removed ... appointment Title"), but in order to figure out the exact changes I'll have to look at the modified properties.
I'd be grateful if you could provide an insight into how you would structure & handle this task. Thank you.
asp.net-core entity-framework-core asp.net-core-signalr
Just wondering: Where exactly does SignalR come into play here?
– poke
Mar 27 at 16:09
You got me, @poke: SignalR is part of the NotificationService, which I have omitted for the question. It obtains a HubContext and sends messages out to the user. The question is more about on how and where exactly these messages should be constructed.
– ExternalUse
Mar 27 at 16:12
add a comment |
I have a controller that modifies appointments in a calendar. I want to use my SignalR hub to notify users à la "User X changed appointmentTitle: List: Property OriginalValue NewValue"
I'm a beginner in C# (Syntax-wise it's ok, but OOP concepts are new); I'm trying to use events to achieve the above.
Below are the handlers and arguments, an extract from the controller and a summary of my questions.
Code is abbreviated!
EventArgs
public class AppointmentChangeEventArgs : EventArgs
public EntityState AppointmentState = EntityState.Unchanged;
public EntityEntry Entity = null;
public ScheduleData Appointment = null;
EventHandler
// 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;
Controller
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();
Questions
- Is it ok to use EntityEntry and EntityState in event arguments?
- for each modified Entry, I can obtain
_dbContext.Entry(modifiedEntry).Properties.Where(x => x.IsModified).ToList();
- but does this belong in the NotificationService class? In order to do that, I'd also need to pass the DbContext over to NotificationService. - Might there be a simpler way to achieve this? Adding and Removing handlers are easy ("User X has added|removed ... appointment Title"), but in order to figure out the exact changes I'll have to look at the modified properties.
I'd be grateful if you could provide an insight into how you would structure & handle this task. Thank you.
asp.net-core entity-framework-core asp.net-core-signalr
I have a controller that modifies appointments in a calendar. I want to use my SignalR hub to notify users à la "User X changed appointmentTitle: List: Property OriginalValue NewValue"
I'm a beginner in C# (Syntax-wise it's ok, but OOP concepts are new); I'm trying to use events to achieve the above.
Below are the handlers and arguments, an extract from the controller and a summary of my questions.
Code is abbreviated!
EventArgs
public class AppointmentChangeEventArgs : EventArgs
public EntityState AppointmentState = EntityState.Unchanged;
public EntityEntry Entity = null;
public ScheduleData Appointment = null;
EventHandler
// 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;
Controller
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();
Questions
- Is it ok to use EntityEntry and EntityState in event arguments?
- for each modified Entry, I can obtain
_dbContext.Entry(modifiedEntry).Properties.Where(x => x.IsModified).ToList();
- but does this belong in the NotificationService class? In order to do that, I'd also need to pass the DbContext over to NotificationService. - Might there be a simpler way to achieve this? Adding and Removing handlers are easy ("User X has added|removed ... appointment Title"), but in order to figure out the exact changes I'll have to look at the modified properties.
I'd be grateful if you could provide an insight into how you would structure & handle this task. Thank you.
asp.net-core entity-framework-core asp.net-core-signalr
asp.net-core entity-framework-core asp.net-core-signalr
asked Mar 27 at 16:06
ExternalUseExternalUse
1,5901 gold badge16 silver badges28 bronze badges
1,5901 gold badge16 silver badges28 bronze badges
Just wondering: Where exactly does SignalR come into play here?
– poke
Mar 27 at 16:09
You got me, @poke: SignalR is part of the NotificationService, which I have omitted for the question. It obtains a HubContext and sends messages out to the user. The question is more about on how and where exactly these messages should be constructed.
– ExternalUse
Mar 27 at 16:12
add a comment |
Just wondering: Where exactly does SignalR come into play here?
– poke
Mar 27 at 16:09
You got me, @poke: SignalR is part of the NotificationService, which I have omitted for the question. It obtains a HubContext and sends messages out to the user. The question is more about on how and where exactly these messages should be constructed.
– ExternalUse
Mar 27 at 16:12
Just wondering: Where exactly does SignalR come into play here?
– poke
Mar 27 at 16:09
Just wondering: Where exactly does SignalR come into play here?
– poke
Mar 27 at 16:09
You got me, @poke: SignalR is part of the NotificationService, which I have omitted for the question. It obtains a HubContext and sends messages out to the user. The question is more about on how and where exactly these messages should be constructed.
– ExternalUse
Mar 27 at 16:12
You got me, @poke: SignalR is part of the NotificationService, which I have omitted for the question. It obtains a HubContext and sends messages out to the user. The question is more about on how and where exactly these messages should be constructed.
– ExternalUse
Mar 27 at 16:12
add a comment |
1 Answer
1
active
oldest
votes
To start off, I would generally recommend you not to use events here. Events are something that may sound very useful but due to the way they work (synchronously), they aren’t really the best way to achieve this in a web context, especially in a primarily asynchronous framework like ASP.NET Core.
Instead, I would recommend you to simply declare your own type, e.g. IAppointmentChangeHandler
like this:
public interface IAppointmentChangeHandler
Task AddAppointment(ScheduleData appointment);
Task UpdateAppointment(ScheduleData appointment);
Task RemoveAppointment(ScheduleData appointment);
Your NotificationService
can just implement that interface to be able to handle those events (obviously just send whatever you need to send there):
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);
And inside of your controller, you just inject that IAppointmentChangeHandler
then and call the actual method on it. That way you have both the controller and the notification service completely decoupled: The controller does not need to construct the type first and you also do not need to subscribe to some events (which you would also have to unsubscribe from at some point again btw). And you can leave the instantiation completely to the DI container.
To answer your individual questions:
Is it ok to use EntityEntry and EntityState in event arguments?
I would avoid using it in a context outside of your database. Both are an implementation detail of your database setup, since you are using Entity Framework here. Not only would this couple your event handlers strongly with Entity Framework (meaning that everyone that wanted to be an event handler would need to reference EF even if they didn’t do anything with it), you are also leaking possibly internal state that may change later (you don’t own the EntityEntry
so who knows what EF does with it afterwards).
for each modified Entry, I can obtain
_dbContext.Entry(modifiedEntry).Properties.Where(x => x.IsModified).ToList();
If you look at your code, you are first calling Add
, Update
or Remove
on your database set; and then you are using some logic to look at some internal EF stuff to figure out the exact same thing really. You could make this a lot less complex if you constructed the AppointmentChangeEventArgs
within those three switch
cases directly.
but does this belong in the NotificationService class? In order to do that, I'd also need to pass the DbContext over to NotificationService.
Does a notification service have anything to do with a database? I would say no; unless you are persisting those notifications into the database. When I think about a notification service, then I expect to be able to call something on it to actively trigger a notification, instead of having some logic within the service to figure out what notifications it could possibly trigger.
Might there be a simpler way to achieve this? Adding and Removing handlers are easy ("User X has added|removed ... appointment Title"), but in order to figure out the exact changes I'll have to look at the modified properties.
Think about it in the simplest way first: Where do you update the values of the database entity? Within that update
case. So at that point, where you are copying over values from the passed object, you can also just check which properties you are actually changing. And with that, you can record easily which properties you need to notify about.
Decouple this completely from EF and you will be a lot more flexible in the long run.
Wow, thanks a lot for taking the time to come up with this. I've got a lot to learn! I'll with this and will report back asap. Please bear with me a moment before I accept the answer.
– ExternalUse
Mar 27 at 16:41
Could you elaborate slightly on "And inside of your controller, you just inject that IAppointmentChangeHandler then and call the actual method on it." - should I register<IAppointmentChangeHandler, NotificationService>()
as a scoped service to use it?
– ExternalUse
Mar 27 at 16:46
1
Yeah, exactly, you can register it like that and then inject the handler interface to use it. It doesn’t need to be scoped since you don’t use the database here though.
– poke
Mar 27 at 17:56
Sorry it took awhile to get back on the question. I've got one more question that I can't figure out with this approach. By not using Events I'm not able to add multiple subscribers easily - so say I'd like to add additional Notification services later, I'd have to go back and change the NotificationService, right? Is there a way to inject multiple instances or sth similar?
– ExternalUse
Apr 11 at 11:29
1
@ExternalUse You could register multiple types asIAppointmentChangeHandler
and then instead of injecting just one handler, inject anIEnumerable<IAppointmentChangeHandler>
to get a list of all registered handlers. And then just loop through those and call the method on each of them.
– poke
Apr 11 at 13:04
|
show 1 more comment
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55381737%2fwriting-an-event-based-signalr-notification-service-using-dbcontext-changetracke%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
To start off, I would generally recommend you not to use events here. Events are something that may sound very useful but due to the way they work (synchronously), they aren’t really the best way to achieve this in a web context, especially in a primarily asynchronous framework like ASP.NET Core.
Instead, I would recommend you to simply declare your own type, e.g. IAppointmentChangeHandler
like this:
public interface IAppointmentChangeHandler
Task AddAppointment(ScheduleData appointment);
Task UpdateAppointment(ScheduleData appointment);
Task RemoveAppointment(ScheduleData appointment);
Your NotificationService
can just implement that interface to be able to handle those events (obviously just send whatever you need to send there):
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);
And inside of your controller, you just inject that IAppointmentChangeHandler
then and call the actual method on it. That way you have both the controller and the notification service completely decoupled: The controller does not need to construct the type first and you also do not need to subscribe to some events (which you would also have to unsubscribe from at some point again btw). And you can leave the instantiation completely to the DI container.
To answer your individual questions:
Is it ok to use EntityEntry and EntityState in event arguments?
I would avoid using it in a context outside of your database. Both are an implementation detail of your database setup, since you are using Entity Framework here. Not only would this couple your event handlers strongly with Entity Framework (meaning that everyone that wanted to be an event handler would need to reference EF even if they didn’t do anything with it), you are also leaking possibly internal state that may change later (you don’t own the EntityEntry
so who knows what EF does with it afterwards).
for each modified Entry, I can obtain
_dbContext.Entry(modifiedEntry).Properties.Where(x => x.IsModified).ToList();
If you look at your code, you are first calling Add
, Update
or Remove
on your database set; and then you are using some logic to look at some internal EF stuff to figure out the exact same thing really. You could make this a lot less complex if you constructed the AppointmentChangeEventArgs
within those three switch
cases directly.
but does this belong in the NotificationService class? In order to do that, I'd also need to pass the DbContext over to NotificationService.
Does a notification service have anything to do with a database? I would say no; unless you are persisting those notifications into the database. When I think about a notification service, then I expect to be able to call something on it to actively trigger a notification, instead of having some logic within the service to figure out what notifications it could possibly trigger.
Might there be a simpler way to achieve this? Adding and Removing handlers are easy ("User X has added|removed ... appointment Title"), but in order to figure out the exact changes I'll have to look at the modified properties.
Think about it in the simplest way first: Where do you update the values of the database entity? Within that update
case. So at that point, where you are copying over values from the passed object, you can also just check which properties you are actually changing. And with that, you can record easily which properties you need to notify about.
Decouple this completely from EF and you will be a lot more flexible in the long run.
Wow, thanks a lot for taking the time to come up with this. I've got a lot to learn! I'll with this and will report back asap. Please bear with me a moment before I accept the answer.
– ExternalUse
Mar 27 at 16:41
Could you elaborate slightly on "And inside of your controller, you just inject that IAppointmentChangeHandler then and call the actual method on it." - should I register<IAppointmentChangeHandler, NotificationService>()
as a scoped service to use it?
– ExternalUse
Mar 27 at 16:46
1
Yeah, exactly, you can register it like that and then inject the handler interface to use it. It doesn’t need to be scoped since you don’t use the database here though.
– poke
Mar 27 at 17:56
Sorry it took awhile to get back on the question. I've got one more question that I can't figure out with this approach. By not using Events I'm not able to add multiple subscribers easily - so say I'd like to add additional Notification services later, I'd have to go back and change the NotificationService, right? Is there a way to inject multiple instances or sth similar?
– ExternalUse
Apr 11 at 11:29
1
@ExternalUse You could register multiple types asIAppointmentChangeHandler
and then instead of injecting just one handler, inject anIEnumerable<IAppointmentChangeHandler>
to get a list of all registered handlers. And then just loop through those and call the method on each of them.
– poke
Apr 11 at 13:04
|
show 1 more comment
To start off, I would generally recommend you not to use events here. Events are something that may sound very useful but due to the way they work (synchronously), they aren’t really the best way to achieve this in a web context, especially in a primarily asynchronous framework like ASP.NET Core.
Instead, I would recommend you to simply declare your own type, e.g. IAppointmentChangeHandler
like this:
public interface IAppointmentChangeHandler
Task AddAppointment(ScheduleData appointment);
Task UpdateAppointment(ScheduleData appointment);
Task RemoveAppointment(ScheduleData appointment);
Your NotificationService
can just implement that interface to be able to handle those events (obviously just send whatever you need to send there):
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);
And inside of your controller, you just inject that IAppointmentChangeHandler
then and call the actual method on it. That way you have both the controller and the notification service completely decoupled: The controller does not need to construct the type first and you also do not need to subscribe to some events (which you would also have to unsubscribe from at some point again btw). And you can leave the instantiation completely to the DI container.
To answer your individual questions:
Is it ok to use EntityEntry and EntityState in event arguments?
I would avoid using it in a context outside of your database. Both are an implementation detail of your database setup, since you are using Entity Framework here. Not only would this couple your event handlers strongly with Entity Framework (meaning that everyone that wanted to be an event handler would need to reference EF even if they didn’t do anything with it), you are also leaking possibly internal state that may change later (you don’t own the EntityEntry
so who knows what EF does with it afterwards).
for each modified Entry, I can obtain
_dbContext.Entry(modifiedEntry).Properties.Where(x => x.IsModified).ToList();
If you look at your code, you are first calling Add
, Update
or Remove
on your database set; and then you are using some logic to look at some internal EF stuff to figure out the exact same thing really. You could make this a lot less complex if you constructed the AppointmentChangeEventArgs
within those three switch
cases directly.
but does this belong in the NotificationService class? In order to do that, I'd also need to pass the DbContext over to NotificationService.
Does a notification service have anything to do with a database? I would say no; unless you are persisting those notifications into the database. When I think about a notification service, then I expect to be able to call something on it to actively trigger a notification, instead of having some logic within the service to figure out what notifications it could possibly trigger.
Might there be a simpler way to achieve this? Adding and Removing handlers are easy ("User X has added|removed ... appointment Title"), but in order to figure out the exact changes I'll have to look at the modified properties.
Think about it in the simplest way first: Where do you update the values of the database entity? Within that update
case. So at that point, where you are copying over values from the passed object, you can also just check which properties you are actually changing. And with that, you can record easily which properties you need to notify about.
Decouple this completely from EF and you will be a lot more flexible in the long run.
Wow, thanks a lot for taking the time to come up with this. I've got a lot to learn! I'll with this and will report back asap. Please bear with me a moment before I accept the answer.
– ExternalUse
Mar 27 at 16:41
Could you elaborate slightly on "And inside of your controller, you just inject that IAppointmentChangeHandler then and call the actual method on it." - should I register<IAppointmentChangeHandler, NotificationService>()
as a scoped service to use it?
– ExternalUse
Mar 27 at 16:46
1
Yeah, exactly, you can register it like that and then inject the handler interface to use it. It doesn’t need to be scoped since you don’t use the database here though.
– poke
Mar 27 at 17:56
Sorry it took awhile to get back on the question. I've got one more question that I can't figure out with this approach. By not using Events I'm not able to add multiple subscribers easily - so say I'd like to add additional Notification services later, I'd have to go back and change the NotificationService, right? Is there a way to inject multiple instances or sth similar?
– ExternalUse
Apr 11 at 11:29
1
@ExternalUse You could register multiple types asIAppointmentChangeHandler
and then instead of injecting just one handler, inject anIEnumerable<IAppointmentChangeHandler>
to get a list of all registered handlers. And then just loop through those and call the method on each of them.
– poke
Apr 11 at 13:04
|
show 1 more comment
To start off, I would generally recommend you not to use events here. Events are something that may sound very useful but due to the way they work (synchronously), they aren’t really the best way to achieve this in a web context, especially in a primarily asynchronous framework like ASP.NET Core.
Instead, I would recommend you to simply declare your own type, e.g. IAppointmentChangeHandler
like this:
public interface IAppointmentChangeHandler
Task AddAppointment(ScheduleData appointment);
Task UpdateAppointment(ScheduleData appointment);
Task RemoveAppointment(ScheduleData appointment);
Your NotificationService
can just implement that interface to be able to handle those events (obviously just send whatever you need to send there):
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);
And inside of your controller, you just inject that IAppointmentChangeHandler
then and call the actual method on it. That way you have both the controller and the notification service completely decoupled: The controller does not need to construct the type first and you also do not need to subscribe to some events (which you would also have to unsubscribe from at some point again btw). And you can leave the instantiation completely to the DI container.
To answer your individual questions:
Is it ok to use EntityEntry and EntityState in event arguments?
I would avoid using it in a context outside of your database. Both are an implementation detail of your database setup, since you are using Entity Framework here. Not only would this couple your event handlers strongly with Entity Framework (meaning that everyone that wanted to be an event handler would need to reference EF even if they didn’t do anything with it), you are also leaking possibly internal state that may change later (you don’t own the EntityEntry
so who knows what EF does with it afterwards).
for each modified Entry, I can obtain
_dbContext.Entry(modifiedEntry).Properties.Where(x => x.IsModified).ToList();
If you look at your code, you are first calling Add
, Update
or Remove
on your database set; and then you are using some logic to look at some internal EF stuff to figure out the exact same thing really. You could make this a lot less complex if you constructed the AppointmentChangeEventArgs
within those three switch
cases directly.
but does this belong in the NotificationService class? In order to do that, I'd also need to pass the DbContext over to NotificationService.
Does a notification service have anything to do with a database? I would say no; unless you are persisting those notifications into the database. When I think about a notification service, then I expect to be able to call something on it to actively trigger a notification, instead of having some logic within the service to figure out what notifications it could possibly trigger.
Might there be a simpler way to achieve this? Adding and Removing handlers are easy ("User X has added|removed ... appointment Title"), but in order to figure out the exact changes I'll have to look at the modified properties.
Think about it in the simplest way first: Where do you update the values of the database entity? Within that update
case. So at that point, where you are copying over values from the passed object, you can also just check which properties you are actually changing. And with that, you can record easily which properties you need to notify about.
Decouple this completely from EF and you will be a lot more flexible in the long run.
To start off, I would generally recommend you not to use events here. Events are something that may sound very useful but due to the way they work (synchronously), they aren’t really the best way to achieve this in a web context, especially in a primarily asynchronous framework like ASP.NET Core.
Instead, I would recommend you to simply declare your own type, e.g. IAppointmentChangeHandler
like this:
public interface IAppointmentChangeHandler
Task AddAppointment(ScheduleData appointment);
Task UpdateAppointment(ScheduleData appointment);
Task RemoveAppointment(ScheduleData appointment);
Your NotificationService
can just implement that interface to be able to handle those events (obviously just send whatever you need to send there):
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);
And inside of your controller, you just inject that IAppointmentChangeHandler
then and call the actual method on it. That way you have both the controller and the notification service completely decoupled: The controller does not need to construct the type first and you also do not need to subscribe to some events (which you would also have to unsubscribe from at some point again btw). And you can leave the instantiation completely to the DI container.
To answer your individual questions:
Is it ok to use EntityEntry and EntityState in event arguments?
I would avoid using it in a context outside of your database. Both are an implementation detail of your database setup, since you are using Entity Framework here. Not only would this couple your event handlers strongly with Entity Framework (meaning that everyone that wanted to be an event handler would need to reference EF even if they didn’t do anything with it), you are also leaking possibly internal state that may change later (you don’t own the EntityEntry
so who knows what EF does with it afterwards).
for each modified Entry, I can obtain
_dbContext.Entry(modifiedEntry).Properties.Where(x => x.IsModified).ToList();
If you look at your code, you are first calling Add
, Update
or Remove
on your database set; and then you are using some logic to look at some internal EF stuff to figure out the exact same thing really. You could make this a lot less complex if you constructed the AppointmentChangeEventArgs
within those three switch
cases directly.
but does this belong in the NotificationService class? In order to do that, I'd also need to pass the DbContext over to NotificationService.
Does a notification service have anything to do with a database? I would say no; unless you are persisting those notifications into the database. When I think about a notification service, then I expect to be able to call something on it to actively trigger a notification, instead of having some logic within the service to figure out what notifications it could possibly trigger.
Might there be a simpler way to achieve this? Adding and Removing handlers are easy ("User X has added|removed ... appointment Title"), but in order to figure out the exact changes I'll have to look at the modified properties.
Think about it in the simplest way first: Where do you update the values of the database entity? Within that update
case. So at that point, where you are copying over values from the passed object, you can also just check which properties you are actually changing. And with that, you can record easily which properties you need to notify about.
Decouple this completely from EF and you will be a lot more flexible in the long run.
answered Mar 27 at 16:29
pokepoke
231k50 gold badges359 silver badges425 bronze badges
231k50 gold badges359 silver badges425 bronze badges
Wow, thanks a lot for taking the time to come up with this. I've got a lot to learn! I'll with this and will report back asap. Please bear with me a moment before I accept the answer.
– ExternalUse
Mar 27 at 16:41
Could you elaborate slightly on "And inside of your controller, you just inject that IAppointmentChangeHandler then and call the actual method on it." - should I register<IAppointmentChangeHandler, NotificationService>()
as a scoped service to use it?
– ExternalUse
Mar 27 at 16:46
1
Yeah, exactly, you can register it like that and then inject the handler interface to use it. It doesn’t need to be scoped since you don’t use the database here though.
– poke
Mar 27 at 17:56
Sorry it took awhile to get back on the question. I've got one more question that I can't figure out with this approach. By not using Events I'm not able to add multiple subscribers easily - so say I'd like to add additional Notification services later, I'd have to go back and change the NotificationService, right? Is there a way to inject multiple instances or sth similar?
– ExternalUse
Apr 11 at 11:29
1
@ExternalUse You could register multiple types asIAppointmentChangeHandler
and then instead of injecting just one handler, inject anIEnumerable<IAppointmentChangeHandler>
to get a list of all registered handlers. And then just loop through those and call the method on each of them.
– poke
Apr 11 at 13:04
|
show 1 more comment
Wow, thanks a lot for taking the time to come up with this. I've got a lot to learn! I'll with this and will report back asap. Please bear with me a moment before I accept the answer.
– ExternalUse
Mar 27 at 16:41
Could you elaborate slightly on "And inside of your controller, you just inject that IAppointmentChangeHandler then and call the actual method on it." - should I register<IAppointmentChangeHandler, NotificationService>()
as a scoped service to use it?
– ExternalUse
Mar 27 at 16:46
1
Yeah, exactly, you can register it like that and then inject the handler interface to use it. It doesn’t need to be scoped since you don’t use the database here though.
– poke
Mar 27 at 17:56
Sorry it took awhile to get back on the question. I've got one more question that I can't figure out with this approach. By not using Events I'm not able to add multiple subscribers easily - so say I'd like to add additional Notification services later, I'd have to go back and change the NotificationService, right? Is there a way to inject multiple instances or sth similar?
– ExternalUse
Apr 11 at 11:29
1
@ExternalUse You could register multiple types asIAppointmentChangeHandler
and then instead of injecting just one handler, inject anIEnumerable<IAppointmentChangeHandler>
to get a list of all registered handlers. And then just loop through those and call the method on each of them.
– poke
Apr 11 at 13:04
Wow, thanks a lot for taking the time to come up with this. I've got a lot to learn! I'll with this and will report back asap. Please bear with me a moment before I accept the answer.
– ExternalUse
Mar 27 at 16:41
Wow, thanks a lot for taking the time to come up with this. I've got a lot to learn! I'll with this and will report back asap. Please bear with me a moment before I accept the answer.
– ExternalUse
Mar 27 at 16:41
Could you elaborate slightly on "And inside of your controller, you just inject that IAppointmentChangeHandler then and call the actual method on it." - should I register
<IAppointmentChangeHandler, NotificationService>()
as a scoped service to use it?– ExternalUse
Mar 27 at 16:46
Could you elaborate slightly on "And inside of your controller, you just inject that IAppointmentChangeHandler then and call the actual method on it." - should I register
<IAppointmentChangeHandler, NotificationService>()
as a scoped service to use it?– ExternalUse
Mar 27 at 16:46
1
1
Yeah, exactly, you can register it like that and then inject the handler interface to use it. It doesn’t need to be scoped since you don’t use the database here though.
– poke
Mar 27 at 17:56
Yeah, exactly, you can register it like that and then inject the handler interface to use it. It doesn’t need to be scoped since you don’t use the database here though.
– poke
Mar 27 at 17:56
Sorry it took awhile to get back on the question. I've got one more question that I can't figure out with this approach. By not using Events I'm not able to add multiple subscribers easily - so say I'd like to add additional Notification services later, I'd have to go back and change the NotificationService, right? Is there a way to inject multiple instances or sth similar?
– ExternalUse
Apr 11 at 11:29
Sorry it took awhile to get back on the question. I've got one more question that I can't figure out with this approach. By not using Events I'm not able to add multiple subscribers easily - so say I'd like to add additional Notification services later, I'd have to go back and change the NotificationService, right? Is there a way to inject multiple instances or sth similar?
– ExternalUse
Apr 11 at 11:29
1
1
@ExternalUse You could register multiple types as
IAppointmentChangeHandler
and then instead of injecting just one handler, inject an IEnumerable<IAppointmentChangeHandler>
to get a list of all registered handlers. And then just loop through those and call the method on each of them.– poke
Apr 11 at 13:04
@ExternalUse You could register multiple types as
IAppointmentChangeHandler
and then instead of injecting just one handler, inject an IEnumerable<IAppointmentChangeHandler>
to get a list of all registered handlers. And then just loop through those and call the method on each of them.– poke
Apr 11 at 13:04
|
show 1 more comment
Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.
Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55381737%2fwriting-an-event-based-signalr-notification-service-using-dbcontext-changetracke%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
Just wondering: Where exactly does SignalR come into play here?
– poke
Mar 27 at 16:09
You got me, @poke: SignalR is part of the NotificationService, which I have omitted for the question. It obtains a HubContext and sends messages out to the user. The question is more about on how and where exactly these messages should be constructed.
– ExternalUse
Mar 27 at 16:12