In previous question folks helped me to solve repository lifetime problem, now there's a question how to make it work nicely in composite service.
let's say i have services:
public class OrderService : IOrderService
{
IRepository<Order> orderRepository;
public OrderService(IRepositoryFactory repositoryFactory)
{
orderRepository = repositoryFactory.GetRepository<Order>();
}
public void CreateOrder(OrderData orderData)
{
...
orderRepository.SubmitChanges();
}
}
public class ReservationService : IReservationService
{
IRepository<Reservation> reservationRepository;
public ReservationService(IRepositoryFactory repositoryFactory)
{
reservationRepository = repositoryFactory.GetRepository<Reservation>();
}
public void MakeReservations(OrderData orderData)
{
...
reservationService.SubmitChanges();
}
}
And now the intersting part - composition service:
public class CompositionService : ICompositionService {
IOrderService orderService;
IReservationService reservationService;
public CompositionService(IOrderService orderService, IReservationService reservationService)
{
this.orderService = orderService;
this.reservationService = reservationService;
}
public void CreateOrderAndMakeReservations(OrderData orderData)
{
using (var ts = new TransactionScope())
{
orderService.CreateOrder(orderData);
reservationService.MakeReservations(orderData);
ts.Complete();
}
}
}
Problem is, that it won't work correctly if IRepositoryFactory lifestyle is transient (because you would get two different datacontexts and that would require distributed transactions to be enabled, which we try to avoid). Any ides how to write this correctly?
My observations:
In general, factories should be singletons. If your factory isn't a singleton, then you are probably just hiding another factory behind it.
Factories are meant for creating objects on demand. Your code simply creates a repository in the constructor, so I don't really see the difference between that and simply making the repository a direct injection parameter in the constructor.
These all seem to me like a workarounds around a more fundamental problem (described in your first question) and these workarounds only make the problem more complicated. Unless you solve the root problem you will end up with a complex dependency schema and a smelly code.
IMO - this is a Distributed Transaction scenario.
In the example you mentioned, OrderService & ReservationService use the same data context is an implementation detail hidden in the code.
I don't think it is correct to pass this knowledge up to the CompositionService by wrapping the service calls in a TransactionScope as now the composition service is aware of the shared data context & so needs to use a TransactionScope to run the code correctly.
In my opinion, the composition service code should look like:
try{
if(orderService.TryCreateOrder(orderData)){
if(reservationService.TryMakeReservation(orderData)){
reservationService.Commit();
orderService.Commit();
}
else{
orderService.TryRollbackOrder(orderData);
throw new ReservationCouldNotBeMadeException();
}
}
else{
throw new OrderCouldNotBeCreatedException();
}
}
catch(CouldNotRollbackOrderServiceException){
// do something here...
}
catch(CouldNotCommitServiceException){
// do something here...
}
In this case, the OrderService.TryCreateOrder method will insert an Order with a PendingReservation status or some other relevant status which indicates that the Order is inserted, but not completed. This state will change on the commits are called on the services (UnitOfWork pattern?)
In this case, the implementation details of the services are completely hidden from the consumer of the service, while composition is also possible, independent on the underlying implementation detail.
HTH.
Related
I'm writing a class that works with an API Client object that sometimes becomes corrupted and has to be recreated from inside the object that uses it. What is the best way to do this using Dependency Injection? I'm hesitant to call the DI framework from inside the class since it makes my code dependent on it.
public class MyObject
{
protected IMyAPIClient Client { get; set; }
public MyObject(IMyAPIClient client)
{
Client = client;
}
protected async Task<ReturnType> Run<ReturnType>(Func<Task<ReturnType>> action, int attempt = 1)
{
try
{
return await action();
}
catch(Exception exception)
{
Client = await GetNewClient();
if(attempt > MAX_ATTEMPTS)
{
throw new Exception($"Failed {attempt} times", exception);
}
return await Run(action, attempt++);
}
}
protected async Task<IMyAPIClient> GetNewClient()
{
// what to do here?
}
}
One solution that I came up with was to implement IMyAPIClient in a class that knows the type of IMyAPIClient and recreates it, thus circumventing the DI framework. I wonder if this is sensible or if there is a better way to do it?
I would first try to fix the problems with the ApiClient. Using workarounds for buggy code is rarely a good idea, but might some times be required for third party code. If the problem is in third party code it might also be advisable to put it in a separate process, otherwise you cannot know if failures have some unexpected side-effects.
To create new objects I would suggest injecting a factory of some kind. Exactly how this is done would depend on the DI/IoC framework. Simplest might be a Func<IMyAPIClient>, but an alternative would be an explicit factory-class. You can chose if the factory should use the IoC container to construct the object, or just construct it directly. Also, some IoC framework will handle factories in some special way, while others might require factories to be registered just like everything else. Check the documentation for your framework to see how factories should be managed.
I would also recommend moving the restart logic to a decorator if possible. That way it is separated from the usage, and should be more flexible in case there are multiple users, or the logic needs to be updated. However, registering decorators can be a bit tricky to ensure they are working correctly, but this will also depend on your IoC framework.
I solved the problem by creating a factory class that creates the IMyAPIClient.
This way I can handle the dependency injection in this factory class without making other code dependent on the DI framework.
public class MyObject
{
protected IMyAPIClientFactory ClientFactory { get; set; }
public MyObject(IMyAPIClientFactory clientFactory)
{
ClientFactory = clientFactory;
Client = clientFactory.CreateClient();
}
protected async Task<ReturnType> Run<ReturnType>(Func<Task<ReturnType>> action, int attempt = 1)
{
try
{
return await action();
}
catch(Exception exception)
{
Client = await clientFactory.CreateClient();
if(attempt > MAX_ATTEMPTS)
{
throw new Exception($"Failed {attempt} times", exception);
}
return await Run(action, attempt++);
}
}
}
I'm working on a classic .Net Framework Web API solution.
I have 3 layers. Let's call them
MVC - with POST, GET, UPDATE, DELETE controllers.
BIZZ - for business with my service class. My service class are king of repositories with CREATE, READ, UPDATE, DELETE and specific methods.
DATA - with POCO and definition of DB context.
I will not develop the EF layer. It is a classic Entity Framework project with POCO.Here is a sample of a Service and with BaseService class
public abstract class Service : IDisposable
{
protected DbContext dbContext = new DbContext();
public void Dispose()
{
dbContext.Dispose();
}
}
Then I have a cart service and a order service. They are similar in their structure so I will only write the code useful for this example.
public class CartService : Service
{
public Cart Create(Cart cart)
{
// Create the cart
}
public Cart Read(Guid id)
{
// Read
}
public Cart Update(Cart cart)
{
// I do some check first then
}
public void Delete(Cart cart)
{
// Delete
}
public void Checkout(Cart cart)
{
// Validation of cart removed in this example
dbContext.Cart.Attach(cart);
cart.DateCheckout = DateTime.UtcNow;
dbContext.Entry(cart).State = EntityState.Modified; // I think this line can be removed
dbContext.SaveChanges();
using (var orderService = new OrderService())
{
foreach (var order in cart.Orders)
{
order.DateCheckout = cart.DateCheckout;
order.Status = OrderStatus.PD; // pending
orderService.Update(order);
}
}
}
}
public class OrderService : Service
{
public Cart Create(Cart cart)
{
// Create the cart
}
public Cart Read(Guid id)
{
// Read
}
public Cart Update(Cart cart)
{
dbContext.Entry(order).State = EntityState.Modified;
dbContext.SaveChanges();
// More process here...
return order;
}
public void Delete(Cart cart)
{
// Delete
}
}
So, I have a service, cart service, that call another service, order service. I must work like this because I cannot simply accept the cart and all orders in it as it is. When I save a new order or update an existing order I must create a record in some other tables in other databases. The code is not in my example. So, I repeat I have a service that call another service and then I have 2 dbContext. At best this just create 2 context in memory, at worst this create exception. Exception like you cannot attach an entity to 2 contexts or this entity is not in context.
Well, I would like all my service use the same context. I suppose you will al tell me to use Dependency Injection. Yes, well ok but I don't want, each time I create a new service have to pass the context. I don't want to have to do that:
public void Checkout(Cart cart)
{
// ...
using (var orderService = new OrderService(dbContext))
{
// ...
}
}
I would like to do something that impact my base service only if possible. A singleton maybe... At this point I can see your face. Yes I know Singleton are soo bad. Yes but i'm doing a IIS Web API. Each request is a new instance. I don't care about the impact of the singleton. And I can load my database by changing the connection string in config file so the benefit of DI is there already. Well, I also know it is possible to have singleton with DI. I just don't know how.
So, what can I do to be sure I share my dbContext with all my services?
Disclaimer: This example is not intended to be a "good" one and certainly does not follow best practices, but faced with an existing legacy code base which from your example already suffers from a number of questionable practices, this should get you past the multiple context issues.
Essentially if you're not already using a IoC Container to perform dependency injection then what you need is to introduce a unit of work to manage the scope of a DbContext where your base Service class provides a DbContext provided by the unit of work. (Essentially a DbContext Registry)
For the unit of work and assuming EF6 I would recommend Mehdime's DbContextScope which is available as a NuGet package. Alternatively you can find the source code on Github and implement something similar without too much trouble. I like this pattern because it leverages the CallContext to serve as the communication layer between the ContextScope (Unit of Work) created by the DbContextScopeFactory and the AmbientDbContextScope. This will probably take a little time to get your head around but it injects very nicely into legacy applications where you want to leverage the Unit of Work and don't have dependency injection.
What it would look like:
In your Service class you would introduce the AmbientDbContextLocator to resolve your DbContext:
private readonly IAmbientDbContextLocator _contextLocator = new AmbientDbContextLocator();
protected DbContext DbContext
{
get { return _contextLocator.Get<DbContext>(); }
}
And that's it. Later as you refactor to accommodate Dependency injection, just inject the AmbientDbContextLocator instead of 'new'ing it up.
Then, in your web API controllers where you are using your services, (not the services themselves) you need to add the DbContextScopeFactory instance..
private readonly IDbContextScopeFactory _contextScopeFactory = new DbContextScopeFactory();
Lastly, in your API methods, when you want to call your services, you need to simply use the ContextScopeFactory to create a context scope. The AmbientDbContextLocators will retrieve the DbContext from this context scope. The context scope you create with the factory will be done in a using block to ensure your contexts are disposed. So, using your Checkout method as an example, it would look like:
In your Web API [HttpPost] Checkout() method:
using (var contextScope = _contextScopeFactory.Create())
{
using(var service = new CartService())
{
service.Checkout();
}
contextScope.SaveChanges();
}
Your cart service Checkout method would remain relatively unchanged, only instead of accessing dbContext as a variable (new DbContext()) it will access the DbContext property which gets the context through the context locator.
The Services can continue to call DbContext.SaveChanges(), but this isn't necessary and the changes will not be committed to the DB until the contextScope.SaveChanges() is called. Each service will have its own instance of the Context Locator rather than the DbContext and these will be dependent on you defining a ContextScope to function. If you call a Service method that tries to access the DbContext without being within a using (var contextScope = _contextScopeFactory.Create()) block you will receive an error. This way all of your service calls, even nested service calls (CartService calls OrderService) will be interacting with the same DbContext instance.
Even if you just want to read data, you can leverage a slightly faster DbContext using _contextScopeFactory.CreateReadOnly() which will help guard against unexpected/disallowed calls to SaveChanges().
When using the ASP.NET Core stack, the tutorial for using EF with it defaults to using DI to provide your DB context, just not with a service layer. That said, it actually does the right thing for this out of the box. I'll give a brief rundown of the bare minimum necessary for this to work, using whatever the latest versions of ASP.NET Core Web API and EF Core were on NuGet at the time of writing.
First, let's get the boilerplate out of the way, starting with the model:
Models.cs
public class ShopContext : DbContext
{
public ShopContext(DbContextOptions options) : base(options) {}
// We add a GUID here so we're able to tell it's the same object later.
public string Id { get; } = Guid.NewGuid().ToString();
public DbSet<Cart> Carts { get; set; }
public DbSet<Order> Orders { get; set; }
}
public class Cart
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Order
{
public string Id { get; set; }
public string Name { get; set; }
}
Then some bare-bones services:
Services.cs
public class CartService
{
ShopContext _ctx;
public CartService(ShopContext ctx)
{
_ctx = ctx;
Console.WriteLine($"Context in CartService: {ctx.Id}");
}
public async Task<List<Cart>> List() => await _ctx.Carts.ToListAsync();
public async Task<Cart> Create(string name)
{
return (await _ctx.Carts.AddAsync(new Cart {Name = name})).Entity;
}
}
public class OrderService
{
ShopContext _ctx;
public OrderService(ShopContext ctx)
{
_ctx = ctx;
Console.WriteLine($"Context in OrderService: {ctx.Id}");
}
public async Task<List<Order>> List() => await _ctx.Orders.ToListAsync();
public async Task<Order> Create(string name)
{
return (await _ctx.Orders.AddAsync(new Order {Name = name})).Entity;
}
}
The only notable things here are: the context comes in as a constructor parameter as God intended, and we log the ID of the context to verify when it gets created with what.
Then our controller:
ShopController.cs
[ApiController]
[Route("[controller]")]
public class ShopController : ControllerBase
{
ShopContext _ctx;
CartService _cart;
OrderService _order;
public ShopController(ShopContext ctx, CartService cart, OrderService order)
{
Console.WriteLine($"Context in ShopController: {ctx.Id}");
_ctx = ctx;
_cart = cart;
_order = order;
}
[HttpGet]
public async Task<IEnumerable<string>> Get()
{
var carts = await _cart.List();
var orders = await _order.List();
return (from c in carts select c.Name).Concat(from o in orders select o.Name);
}
[HttpPost]
public async Task Post(string name)
{
await _cart.Create(name);
await _order.Create(name);
await _ctx.SaveChangesAsync();
}
}
As above, we take the context as a constructor parameter to triple-check it's what it should be; we also need it to call SaveChanges at the end of an operation. (You can refactor this out of controllers if you want to, but they'll work just fine as units of work for now.)
The part that ties this together is the DI configuration:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// Use whichever provider you have here, this is where you grab a connection string from the app configuration.
services.AddDbContext<ShopContext>(options =>
options.UseInMemoryDatabase("Initrode"));
services.AddScoped<CartService>();
services.AddScoped<OrderService>();
}
AddDbContext() defaults to registering a DbContext to be created per-request by the container. Web API provides the AddControllers method that puts those into the DI container, and we also register our services manually.
The rest of Startup.cs I've left as-is.
Starting this up and opening https://localhost:5001/shop should log something like:
Context in CartService: b213966e-35f2-4cc9-83d1-98a5614742a3
Context in OrderService: b213966e-35f2-4cc9-83d1-98a5614742a3
Context in ShopController: b213966e-35f2-4cc9-83d1-98a5614742a3
with the same GUID for all three lines in a request, but a different GUID between requests.
A little additional explanation of what goes on above:
Registering a component in a container (using Add() and such above) means telling the container those components exist and that it should create them for you when asked, as well as what identifiers they're available under and how to create them. The defaults for this are more or less "make the component available as its class, and create it by calling its one public constructor, passing other registered components into it" - the container looks at the constructor signature to figure this out.
"Scoped" in an ASP.NET Core app means "per-request." I think in this case one could also use services with a transient lifetime - a new one created every time it's needed, but they'll still get the same DbContext as long as they're created while handling the same request. Which one to do is a design consideration; the main constraint is that you can't inject shorter-lived components into longer-lived components without having to use more complex techniques, which is why I favour having all components as short-lived as possible. In other words, I only make things longer-lived when they actually hold some state that needs to live for that time, while also doing that as sparingly as possible because state bad. (Just recently I had to refactor an unfortunate design where my services were singletons, but I wanted my repositories to be per-request so as to be able to inject the currently logged in user's information into the repository to be able to automatically add the "created by" and "updated by" fields.)
You'll note that with support for doing things this way being built-in to both ASP.NET Core and EF Core, there's actually very little extra code involved. Also, the only thing needed to go from "injecting a context into your controllers" (as the tutorial does) to "injecting a context into services that you use from your controllers" is adding the services into DI - since the controller and context are already under DI, anything new you add can be injected into them and vice versa.
This should give you a quick introduction into how to make things "just work" and shows you the basic use case of a DI container: you declaratively tell it or it infers "this is an X", "this is an Y", "this is a Z and it needs to be created using an X and a Y"; then when you ask the container to give you a Z, it will automagically first create an X and Y, then create Z with them. They also manage the scope and lifetime of these objects, i.e. only create one of a type for an API request. Beyond that it's a question of experience with them and familiarity with a given container - say Ninject and Autofac are much more powerful than the built-in one - but it's variations on the same idea of declaratively describing how to create an object possibly using other objects (its dependencies) and having the container "figure out" how to wire things together.
I am interested in the architectural solution of the following moment.
I have:
public class GenericRepository<T> : IDisposable {
public GenericRepository(ISession session){
_session = session;
};
public T InsertAsync(T entity){...};
public IQueryable<T> Read(){...};
public T UpateAsync(T entity){...};
public void DeleteAsync(T entity){...};
public Task Commit(){
return _session.Transaction.Commit();
};
public void Dispose(){
if(_session.Transaction.IsActive){
_session.Transaction.Rollback();
}
};
}
public class UserService{
public UserService(GenericRepository<User> repository){...}
public long CreateUser(string userName){
...
_repository.Commit(); // [1]
};
}
public class OrganizationService{
public OrganizationService(GenericRepository<Organization> repository){...}
public int CreateOrganization(string code){
...
_repository.Commit(); // [2]
};
}
The following registration is used:
services.AddScoped<ISession>(x => x.GetRequiredService<NHSessionProvider>().OpenSession());
services.AddScoped(typeof(GenericRepository<>));
services.AddScoped<UserService>();
services.AddScoped<OrganizationService>();
These CreateOrganization and CreateUser can be used independently in any parts of the code:
public IActionResult Post([FromServices] OrganizationService service, [FromBody] string code){
service.CreateOrganization(code);
return Ok();
}
public IActionResult Post([FromServices] UserService service, [FromBody] string userName){
service.CreateUser(userName);
return Ok();
}
However, now I have a new service:
public class MyBillingService{
public MyBillingService(GenericRepository<Contractor> repository, OrganizationService organizationService, UserService userService){...}
public int CreateNewContractor(string organizationCode, string userName){
...
_organizationService.CreateOrganization(organizationCode);
...
_userService.CreateUser(userName);// [3]
...
_repository.Commit(); // [4]
}
}
In this implementation, CreateOrganization and CreateUser have their own transactions, and if [3] throws an exception, then the organization will be created anyway.
Ok, because ISession is registered as Scoped, then I can delete _repository.Commit from CreateOrganization and CreateUser([1] and [2]). In this case, [4] will be responsible for committing all changes.
But what then to do when OrganizationService and UserService are used independently? After all, now they have become non-independent services and cannot save data without delegating the commit of changes to some other service:
public IActionResult Post([FromServices] UserService service, [FromServices] TransactionService transaction, [FromBody] string userName){
service.CreateUser(userName);
transaction.Commit();
return Ok();
}
As far as this decision is a good one?
Transactions requires a unit of work. There is no other way to coordinate repositories. The reason you're facing issues here is that your entire design is wrong.
First and foremost, you should not have these repositories at all. You're using EF Core, which is an ORM, and already implements the repository and unit of work patterns. Using an ORM is opting to use a third-party library for your DAL. Wrapping your own DAL layer around that is pointless and imposes needless maintenance and testing costs on your application with zero benefit. Your services should depend on your context directly.
Then, services should be self-contained units of functionality. If they depend on other services, you're doing it wrong. The service should correspond with a particular subdomain of your application. If users and organization need to be managed together transactionally, then you should have one service that encompasses both.
Alternatively, if you want/need to keep the two separate, then you would need to incorporate the concept of sagas.
So I've started to move more towards what Chris mentioned in his answer and use the ISession directly, but I have used a generic repository in the past. Your repos can't correctly handle transactions that are already started.
So my generic repo has a couple of methods
protected virtual TResult Transact<TResult>(Func<TResult> func)
{
if (_session.Transaction.IsActive)
return func.Invoke();
TResult result;
using (var tx = _session.BeginTransaction(IsolationLevel.ReadCommitted))
{
result = func.Invoke();
tx.Commit();
}
return result;
}
protected virtual void Transact(System.Action action)
{
Transact(() =>
{
action.Invoke();
return false;
});
}
Then the methods that are implementing the repo functionality look like this
public bool Remove(T item)
{
Transact(() => _session.Delete(item));
return true;
}
This allows the method to use an existing Transaction if it is already started, otherwise create your transaction for this work.
You also should not have a Dispose in your repo since you don't own the reference to ISession. It's life cycle should be handled by whoever created that instance.
The generic repository also shouldn't have commit functionality except when it is explicitly starting a new transaction. So now you need to have something that handles starting and committing said transaction. In a web scenario you are typically in a session per request scenario. This would mean you are creating your session in BeginRequest and disposing of it in EndRequest. I then use a transaction attribute to manage creating transactions prior to executing the controller action and commit/rollback after the execution of the controller method.
I have been trying to create a Repository Pattern along with Dependency injection, But Looks like I am missing some simple step. Here is my code
public class HomeController
{
private readonly ILoggingRepository _loggingRepository;
public HomeController(ILoggingRepository loggingRepository)
{
_loggingRepository = loggingRepository;
}
public void MyMethod()
{
string message = "MyMessage Called";
_loggingRepository .LogMessage(message);
}
}
// ILoggingRepository.cs
public interface ILoggingRepository
{
void LogMessage(string message);
}
// LoggingRepository.cs
public class LoggingRepository : ILoggingRepository
{
public void LogMessage(string message)
{
using (var dbContext = new DbContext())
{
var serviceLog = new Log() { Message = message, Logged = DateTime.UtcNow };
dbContext.Logs.Add(serviceLog);
dbContext.SaveChanges();
}
}
}
This works perfectly all right so far, but the problem arises when i make more than one repository calls.
Now I know that Entity framework 6.0 has inbuilt unit of work representation so I didn't created a UnitofWork Interface or class
But the problem appears when I do something like this in two different transactions. Lets say
Area area = _areaRepository.GetArea(); // Line 1
area.Name = "NewArea"; // Line 2
_areaRepository.SaveArea(area); // Line 3
now because it _areaRepository creates a new DbContext in Line 3, it doesn't changes the name of area as it doesn't consider EntityState.Modified
I have to explicitly set that, which isn't correct.
So I guess I need to do all this in single Transaction, Where I am doing wrong here ?
What is the correct and best way to achieve this, Should I inject my DbContext also into the repository?
This is how I doit all times:
If dont use Repository or Unit of Work layers, because Entity Framework db Context already implements those patterns. So, I only have a Service layer:
public interface IBaseService<VO, ENT>{
IQueryable<VO> GetAll();
VO Get(object id);
}
public abstract class BaseService<VO, ENT> : IBaseService<VO, ENT>{
MyContext db;
public BaseService(MyContext db){
this.db = db;
}
public IQueryable<VO> GetAll(){
return db.Set<ENT>().ProjectTo<VO>();
}
}
A service class have a dbContext injected in the constructor. This classes are located in a Service library. Then, how the dbContext and the service are resolved is a problem of the project who will be using them. The ProjectTo method is an extension for IQueryable from the Automapper Nuget. For example:
A Windows Service needs all services instance in the same thread shares the same dbContext. So, in the windows service project, I use Ninject https://www.nuget.org/packages/Ninject/4.0.0-beta-0134, this library is a dependency resolver, wich I use to configure how dependencies are builded, creating a Kernel, like this:
var kernel = new StandardKernel();
kernel.Bind<MyContext>().ToSelf().InThreadScope();
kernel.Bind<IServiceImplInterface>().To<ServiceImplClass>().InThreadScope();
I you are creating a Web project, you will need to install a aditional nuget (Ninject.WebCommon, Ninject.Web.COmmon.WebHost, Ninject.MVC5) to provide a .InRequestScope() method to the binding configuration, like this:
var kernel = new StandardKernel();
kernel.Bind<MyContext>().ToSelf().InRequestScope();
kernel.Bind<IServiceImplInterface>().To<ServiceImplClass>().InRequestScope();
You need setup those kernel when the app startup. In a web project is in the global.asax, in a windows service project, should be in the Service constructor:
You can visit www.ninject.org/learn.html to learn more about ninject. But, there are othres like Autofac or Caste Windsor, it is up to you. If you like to keep using the repository pattern, just use Ninject inject them into the Service layer, like i did with the dbContext.
The best approach is to have one instance of DbContext, injecting it on each repository implementation. That way you will have a single instance of the database context, so EF will be able to detect changes on the entity objects.
If you need to use isolated dbContexts as in your example, then you need to explicitly set the state of the object as Modified.
Depending on the type of project, you should set the context on a specific scope. For example, for web applications one option is to use instance per Web request (per lifetime scope). Check this url where you can see a good explanation of the different instance scopes.
The using statement simply creates a new scope, executing the Dispose() method after the code block. EF does a lot on the background to maintain the UoW and state of the objects, but in your case, with the using, you are not using this fature.
First, a DbContext is a repository. If you want to wrap it in a custom repository, they should have the same lifecycle.
Second, your Unit-of-work is your controller. The repository should be scoped to unit-of-work.
This means that your repository needs to be Disposable, since the DbContext is.
So something like:
public interface ILoggingRepository : IDisposable
{
void LogMessage(string message);
}
// LoggingRepository.cs
public class LoggingRepository : ILoggingRepository
{
MyDbContext db;
public LoggingRepository(MyDbContext db)
{
this.db = db;
}
public void Dispose()
{
db.Dispose();
}
public void LogMessage(string message)
{
var serviceLog = new MonitoringServiceLog() { Message = message, Logged = DateTime.UtcNow };
db.MonitoringServiceLogs.Add(serviceLog);
db.SaveChanges();
}
}
If your ILoggingRepository wan't a database, it might be a file or something else that is expensive to create or open and needs to be closed.
I am using the Entity framework for the first time, and would like to know if I am using in the best practice.
I have created a separate class in my business logic which will handle the entity context. the problem I have, is in all the videos I have seen they usually wrap the context in a using statement to make sure its closed, but obviously I can't do this in my business logic as the context will be closed before I can actually use it?
So is this ok what I'm doing? A couple of examples:
public IEnumerable<Article> GetLatestArticles(bool Authorised)
{
var ctx = new ArticleNetEntities();
return ctx.Articles.Where(x => x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
}
public IEnumerable<Article> GetArticlesByMember(int MemberId, bool Authorised)
{
var ctx = new ArticleNetEntities();
return ctx.Articles.Where(x => x.MemberID == MemberId && x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
}
I just want to make sure I'm not building something that's going to die when a lot of people use it?
It really depends on how to want to expose your repository/data store.
Not sure what you mean by "the context will be closed, therefore i cannot do business logic". Do your business logic inside the using statement. Or if your business logic is in a different class, then let's continue. :)
Some people return concrete collections from their Repository, in which case you can wrap the context in the using statement:
public class ArticleRepository
{
public List<Article> GetArticles()
{
List<Article> articles = null;
using (var db = new ArticleNetEntities())
{
articles = db.Articles.Where(something).Take(some).ToList();
}
}
}
Advantage of that is satisfying the good practice with connections - open as late as you can, and close as early as you can.
You can encapsulate all your business logic inside the using statement.
The disadvantages - your Repository becomes aware of business-logic, which i personally do not like, and you end up with a different method for each particular scenario.
The second option - new up a context as part of the Repository, and make it implement IDisposable.
public class ArticleRepository : IDisposable
{
ArticleNetEntities db;
public ArticleRepository()
{
db = new ArticleNetEntities();
}
public List<Article> GetArticles()
{
List<Article> articles = null;
db.Articles.Where(something).Take(some).ToList();
}
public void Dispose()
{
db.Dispose();
}
}
And then:
using (var repository = new ArticleRepository())
{
var articles = repository.GetArticles();
}
Or the third-option (my favourite), use dependency injection. Decouple all the context-work from your Repository, and let the DI container handle disposal of resources:
public class ArticleRepository
{
private IObjectContext _ctx;
public ArticleRepository(IObjectContext ctx)
{
_ctx = ctx;
}
public IQueryable<Article> Find()
{
return _ctx.Articles;
}
}
Your chosen DI container will inject the concrete ObjectContext into the instantiation of the Repository, with a configured lifetime (Singleton, HttpContext, ThreadLocal, etc), and dispose of it based on that configuration.
I have it setup so each HTTP Request gets given a new Context. When the Request is finished, my DI container will automatically dispose of the context.
I also use the Unit of Work pattern here to allow multiple Repositories to work with one Object Context.
You may have also noticed I prefer to return IQueryable from my Repository (as opposed to a concrete List). Much more powerful (yet risky, if you don't understand the implications). My service layer performs the business logic on the IQueryable and then returns the concrete collection to the UI.
That is my far the most powerful option, as it allows a simple as heck Repository, the Unit Of Work manages the context, the Service Layer manages the Business Logic, and the DI container handles the lifetime/disposal of resources/objects.
Let me know if you want more info on that - as there is quite a lot to it, even more than this surprisingly long answer. :)
I would have the ctx as a private variable within each class, then create a new instance of this each time and then dispose when finished.
public class ArticleService
{
private ArticleEntities _ctx;
public ArticleService()
{
_ctx = new ArticleEntities();
}
public IEnumerable<Article> GetLatestArticles(bool Authorised)
{
return _ctx.Articles.Where(x => x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
}
public IEnumerable<Article> GetArticlesByMember(int MemberId, bool Authorised)
{
return _ctx.Articles.Where(x => x.MemberID == MemberId && x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
}
public void Dispose()
{
_ctx.Dispose();
_ctx = null;
}
}
Then when calling this.
ArticleService articleService = new ArticleService();
IEnumerable<Article> article = articleService.GetLatestArticles(true);
articleService.Dispose(); // killing the connection
This way you can also add/update other objects within the same context and call a save method which saves any changes to the db through the Entity.
In my experience this code is not good, because you lose the capacity to navigate relationships through navigation properties.
public List<Articles> getArticles( ){
using (var db = new ArticleNetEntities())
{
articles = db.Articles.Where(something).ToList();
}
}
Using this approach you can't use the following code because a.Members is always null( db context is close and cant get data automatically).
var articles = Data.getArticles();
foreach( var a in articles ) {
if( a.Members.any(p=>p.Name=="miki") ) {
...
}
else {
...
}
}
}
Using only a global db context is a bad idea because you must use a delete changes function
in a point of your application yo do this but don't save changes and close the window
var article= globalcontext.getArticleByID(10);
article.Approved=true;
then in another point of application you make some operation and save
//..... something
globalcontext.saveChanges();
in this case previous article approved property is set to modified by entity framework. When you save, approved is set true!!!
Best approach for me is use 1 context per class
You can pass context to another external method if you need
class EditArticle {
private DbEntities de;
private currentAricle;
public EditArticle() {
de = new DbEntities; //inizialize on new istance
}
loadArticleToEdit(Articele a){
// a is from another context
currentArticle= de.Article.Single(p=>p.IdArticle==a.IdArticle){
}
private saveChanges(){
...
pe.saveChanges();
}
}
What you can also do is store your context at a higher level.
E.g., you can have a static class storing the current context:
class ContextManager
{
[ThreadStatic]
public static ArticleEntities CurrentContext;
}
Then, somewhere outside you do something like this:
using (ContextManager.CurrentContext = new ArticleEntities())
{
IEnumerable<Article> article = articleService.GetLatestArticles(true);
}
Then, inside the GetLastestArticles, you just use the same ContextManager.CurrentContext.
Of course, this is just the basic idea. You can make this a lot more workable by using service providers, IoC and such.
You can start preparing Entity Framework from data access layer by creating a generic repository class for all required Entity Framework functions. Then you can used it in Business layer (Encapsulated)
Here are the best practices that I have used for Entity Framework in data, business, and UI layers
Techniques used for this practice:
Applying SOLID architecture principles
Using Repository design pattern
Only one class to go (and you will find it ready)