In previous blog post we described an architecture when an unit of work is used at the
service level to access the database using entity framework (EF). EF has a
DbContext that has a connection to a database, how is it witch the lifecycle of
this connection in our case? We use DI, our DI injects an instance of uow to
AuditService during the creation of the service. The service has no control
over the instantiation of uow, the uow dies when the service dies. This happens
after the service is picked up by garbage collector. This is not really nice.
The recommended way is to have the connection open only for needed minimum.
This is usually done in using blocks. Using works with IDisposable interface,
our unit of work should implement the IDisposable interface. Ok, this is easy,
but should the AuditService control the lifetime of Uow? (= using block on
uow?). If yes, we cannot use DI for putting together the AuditService, instead
a Service Locator pattern should be used, when the AuditService asks the DI container
for an instance of uow explicitly, puts it to using blocks, this way the uow is
disposed after each call. Great for connection savings, but we lose the
positive effects of orm. I mean that having the uow live during the whole
lifetime of the service gives us caching (EF is not going back to database for
an entity which it had already read to its context), tracks back the IDs of
newly inserted records after calling SaveChanges on context, theoretically we
can combine more calls on the service and call the SaveChanges only once = one
trip to Db – our example is not prepared for it, we don’t have SaveChanges
exposed on the service layer. Closing the uow in every call of the service
prevents this.
What are
the alternatives? If we have a web application, we can explicitly dispose in
some page lifecycle event every instance created on the page during the
request. This is extra work (we need to implement some mechanism that tracks
which instance to dispose after the request finishes – a registration class
that tracks disposable instances and is somehow integrated to DI mechanism?).
In case of web applications after the request goes out of scope, the GC
collects every instance created during this request (GC is not calling dispose,
so if we let it to GC probably we don’t need to implement IDisposable). Problem is that this takes some time. If this
is a highly loaded site, this could be problem. EF has 100 concurrent
connections, the 101 will wait for a connection to be disposed. We can also
implement IDisposable on AuditService (it will call Dispose on its uow) and let
the client use the AuditService in using blocks – I like this better compared
to disposing uow inside every call of the AuditService, but imagine a http
request inside a web application, imagine that for some reason multiple
instances of AuditService are used in a request, each of them have a private instance of uow with
dbcontext. Logging in this case (even inside of one http request which is
usually perceived as a business transaction boundary) is done independently at
each instance of AuditService (they have separate dbcontext, what one
AuditService saves, the other one is not seeing etc.). Wouldn’t it be nicer if
they can share one instance of uow inside a http request which is their
business transaction boundary? If yes, we cannot use the dispose approach at
the level of AuditService. (it will dispose the shared instance of uow and
dbcontext). How to share an instance of uow in one http request is described
here.(http://coder21.blogspot.cz/2014/03/sharing-instance-of-uow-inside-one-http.html).
Wrap up:
- If you don’t plan in future to encapsulate multiple calls on service level into a business transaction (= each call to a method of service is a business transaction) and you don’t need a context sharing between multiple instances of services than use the service locator pattern to get an instance of uow inside a service and dispose the uow in each method of the service. This is the simplest solution, equivalent to the current implementation with ADO.NET DAL as described at the first blog post of this series (http://coder21.blogspot.cz/2014/03/entity-framework-repositories-unit-of.html).
- If encapsulating multiple calls on service to one business transaction is required but sharing of context is not important, implement IDisposable on the Service layer to (AuditService) and let the client explicitly dispose the service after it is not required. In this case you don’t need to implement service locator inside the service, classical DI is what you want (DI container will inject automatically the uow to service).
- If you want to share context (mainly web applications http request) look for your DI options that can support objects scoping to a web request (most of them does have such a capability), don’t implement (or use) Dispose at the service level, don’t use service locator at service level (let the DI container inject the uow). You need to find a way how to dispose the instances of uow after the request finishes (web apps), you can let it to GC or implement something that tracks the disposable instances created during the request. (this is custom implementation dependent on the particular DI container, the way how you obtain the instances from the DI, the web framework you use etc. – you would need this definetly if you have a highly loaded site).
I prefer
the third approach, the DI container puts the instances together, not using
service locator for this, using a DI configuration option to share the context
in one http request and let the GC do the dispose the context (the last thing
is not my favorite, but currently it fits my requirements, later I can write an
implementation of explicitly releasing the disposable objects in a request).
Žádné komentáře:
Okomentovat