-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Events and interception (aka lifecycle hooks) #626
Comments
I started prototyping some ideas as a part of my investigation of setting SQLite pragma options when a connection opens. It seems this feature would be generally useful in solving several other feature requests. Can we considering moving this off backlog? |
I think we need @divega in the office to design this one, the all up feature is pretty large and wide-reaching. We also have some higher priority things to work on first. If you are specifically looking at connection pre-amble then I think we can treat that as a smaller feature that we work on now. |
Will this make it into the next release? |
@rowanmiller In terms of an EF Core ObjectMaterialized style hook, can you give me some pointers as to where I can tackle this as of now? |
Permission validation and filtering are other scenarios that could be attained with lifecycle hooks (see #6440). |
Will this also take care of the following scenario? I'd like to add an entity to a DbContext (to be inserted or updated etc), but also subscribe to be notified once the entity has been saved, so i can handle taking its newly updated values (i.e the database generated values that get poplated after a SaveChanges() - like it's ID etc) and do something with them. var dbContext = GetExistingDbContextInstance();
var newItem = new SomeEntity();
dbContext.SomeEntities.Add(newItem);
// I am quite far down in an object graph, and SaveChanges() will eventually be called
// later on by something higher up, co-ordinating this transaction. I'd like to be notified
// here though once SaveChanges() has been called and this entity has been persisted, so I can grab the
// the updated entity values.
// dbContext.OnceSaved(newItem, ()=>{ // ooh someEntity.Id is now populated! }) Something along the lines of:
Where the callback would be invoked after dbContext.SaveChanges() is called. |
@dazinator I'm currently working on an extension project for EFCore (https://github.com/Antaris/EntityFrameworkCoreExtensions) which will allow you to do what you want, eventually. I have a number of working hooks including change tracking, value materialization, querying and storage. It's still a WIP though. |
@rowanmiller What's the expected release date on this? It's been open for almost 2-1/2 years |
Is there a OnModelCreating override implemented, or smth along this issue: #9330 ? |
is there any interceptor available that allow audit feature when using ExecuteUpdate, ExecuteDelete? |
@xaviergxf command interceptors should work for ExecuteUpdate/Delete just like for any other EF database operation. |
Done in 2.1
Done in 3.1
Done in 5.0
Done in 6.0
Done in 7.0
Backlog
Note: below is a copy of a very old EF specification and reflects thinking from several years ago. A lot of things aren't valid anymore.
We define EF Core lifecycle hooks as the general feature that enables an application or library to sign up to be invoked or notified whenever certain interesting conditions or actions occur as part of the lifecycle of entities, properties, associations, queries, context instances, and other elements in the Entity Framework stack.
For example:
DbConnection
is opened (to use features such as SQL Server App Role)The need for lifecycle hooks
We want to enable customers to write business logic that triggers in the different stages of the lifecycle of these objects, following well factored coding patterns. We also want framework writers to be able to use these hooks to extend EF Core in useful ways.
In previous versions of Entity Framework we already exposed a few lifecycle hooks. For instance, we had the
AssociationChanged
andObjectStateManagerChanged
events since the first version, and theObjectMaterialized
event was added in EF4. Up until EF6.x many of the existing hooks are not exposed in the DbContext API. In EF6 we also added several low level extensibility points in Interception that can be used too as lifecycle hooks.There is a continuum of capabilities related and overlapping with lifecycle hooks, e.g.:
Target customers
Goals & Principles
A brief survey of hooking mechanisms
There are not only different interesting conditions or actions an application may need to listen to, but also different kinds of mechanisms to implement hooks that present distinctive characteristics along the following dimensions:
Note: We are looking for criteria that will help us choose the best type of hook for each extensibility point, as well as for the possibility of defining unified hook mechanisms that we can leverage to support different patterns with the same framework code.
.NET Events
Events are the most common hook pattern that almost every API in .NET uses. Events are messages sent by sender object to one or more receiver objects through a multicast delegate that acts as a dispatcher. Among the characteristics of events, they support runtime subscribe/unsubscribe, multiple listeners, and are relatively easy to use. Events can be slower than other hook mechanisms, but they have the advantage of being very discoverable (they are usually public members on the sender object) and familiar to customers. Events also provide a standard way to model cancellable actions with CancelEventArgs.
Virtual methods
Virtual methods require the application code to declare a derived type and override the method. Virtual methods provide better performance than events but are slower than regular method invocation. Virtual methods are easy to discover and provide a very nice model for overriding/customizing default behavior and chaining with subsequent derivate types. Visual Studio provides a nice Intellisense experience for virtual methods: when you write the overrides keyword in C#, Intellisense provides the list of all the virtual methods available.
Delegates
A more efficient alternative to events, regular (non-multicast) delegates can also be used as a hook. Users can normally provide some implementation of a predefined delegate signature (usually a Func<T…> or Action<T…> that can be implemented as a regular method, and anonymous method or a lambda expression) as a parameter to a framework method, as the return type from a method or as a property. Then the delegate is invoked by the framework at appropriate times. While delegates are often compared to strongly typed function pointers, they in fact are very flexible with regards to the signature (they support variance).
Partial methods
Partial methods were introduced in .NET 3.5 as a means to extend generated code in separate partial classes. Partial methods are void methods that are both defined and invoked in the right places in generated code. Users can choose to provide the implementation of partial methods in a separate partial class, and the compiler will resolve the partial method to the implementation provide by the user. When the implementation of a partial method is not provided, all calls to the method and its definition are removed by the compiler. Since partial methods are either turned into regular methods or removed, the mechanism is extremely efficient. Partial methods are discoverable because Visual Studio provides a nice Intellisense experience for them. Similar to virtual methods, once you write the keyword partial inside the partial class, Visual Studio editor will list all the partial method definitions that haven’t been implemented.
Magic methods
The concept of magic methods is that the user can write a method that follows a particular naming convention and signature, and a framework component will make sure the method will get invoked automatically at runtime. Usually an expression is compiled at runtime to produce a delegate that be used to invoke the method multiple times very efficiently. There is some runtime overhead in compiling the expression, but this is paid only once. Magic methods can be instance or static method. A common practice for magic methods, when code generation is involved, is to provide the declaration of the magic method as a partial method. Although no actual calls are generated, the partial method that gets an implementation will be compiled into the assembly so that it can be invoked at runtime. The only reason for this is the Intellisense experience you get.
Magic methods can be used to override default behaviors but it is necessary to expose a public method that implements the default behavior so that the user has the option to invoke this method from within the magic method if he doesn’t want to completely override it.
LINQ to SQL uses this mechanism pervasively.
Attributed methods
Similar to magic methods, an instance or static method with the right signature can decorated with a special attribute that specifies a runtime role for this method. The runtime examines types for the presence of these attributes and registers the method to be executed on the occurrence of certain conditions.
WCF Data Services uses this pattern pervasively.
Listener interfaces
This approach consists on defining a class that implements a custom interface provided by the framework and at a later point register an instance of this class as a listener for particular events. Usually, the interface defines one or more methods with a very specific purpose, but the listener class can be a composite of multiple interfaces. Discoverability can be improved in this programing model with a base listener interfaces and by placing all the related interfaces under the same namespace.
IObservable<T>
In many situations a mechanism is required to handle a stream of asynchronous events coming from the same source. IObservable<T> is an analog to IEnumerabe<T> that can be used to represent this kind of source.
Context hooks vs. entity and property hooks
DbContext provide a very good place for us to focus when defining extensibility hooks for anything that has to do with the functions that they encompass:
But for hooks that are specific to the lifecycle of entity or complex types, properties, etc., other options exist:
The one potential issue with this approach is that the hook configuration would become part of the model and therefore it would not be possible to change it once the context object has been instantiated. This might be an acceptable limitation however, since any necessary changes in behavior can be coded into the listener class itself. Making the configuration of hooks immutable also has the advantage of allowing for compiling the invocations to listeners into efficient delegates.
EF requirements for a hooking mechanims
We are trying to find a design that has the following characteristics:
This requires further analysis, but it seems that it would be reasonable to support a mix of hook mechanisms (but not too many) to optimize for different scenarios.
The current thinking is that we would use a single generic class to represent a hook. The sender should just need to call a method or instantiate a class and call a method, nothing much more complex than the typical “On[EventName]” pattern, and the hook class would make sure all the hook handlers are invoked.
We are also considering building a convention system for wiring up hook handlers in the entity types.
When designing this we should take advantage of any opportunity to reuse some hook mechanism as building blocks for others. For instance:
Lifecycle hooks list
The following is an incomplete list that presents various hooks we could consider adding. Intentionally the list does not try to be specific on each hook about certain details:
Existing hooks
Some open issues:
The text was updated successfully, but these errors were encountered: