Dependency Injection for Long-Lived Objects in Web API? - c#

I'm putting together a REST service using ASP.NET Web API & Ninject, though I suspect this might be a more general IoC question than anything specific to my IoC framework. I have a number of objects that need to access a simple cache of User entities:
public class UserCache
{
private IList<User> users;
private IUserRepositoryFactory factory;
[Inject]
public UserCache(IUserRepositoryFactory factory)
{
this.factory = factory;
this.users = new List<User>();
}
public void Add(int id)
{
IUserRepository repo = factory.Create(new TestContext());
this.users.Add(repo.Get(id));
}
public int Count { get { return this.users.Count; } }
}
In practice, the cache is read-through, and will fill itself with User entities using a UserRepository (and associated IUserRepository interface):
public class UserRepository : IUserRepository
{
private readonly TestContext context;
public UserRepository(TestContext context)
{
this.context = context;
}
public User Get(int id)
{
return new User() { Name = "Test User" };
}
}
The cache is long-lived and shared across the entire application. My question is this: I want to use my UserRepository to pull User entities from my database. This repository needs to be injected into the cache somehow, or instantiated using a factory.
The trick is, the only way I've been able to both a) create the cache such that Ninject will inject its dependencies and b) have access to the cache throughout the same is to bind the cache in singleton scope and inject it into objects that need access to it:
kernel.Bind<TestContext>().ToSelf();
kernel.Bind<UserCache>().ToSelf().InSingletonScope();
...and then in a controller (for example):
[Inject]
public UserCache Cache { get; set; }
My question is, is this the best way to treat long-lived objects that require injection? Or is there some better way that I'm missing? I don't want to give the cache (or any other objects like it) direct access to the Ninject kernel.

Isn't this supposed to be the other way around? You should use IUserRepository in your controllers and the repository under the hood should fetch the data from cache (better if done using an interceptor) if it is already cached, otherwise should hit the database.
That way you don't have to worry about lifecycle of the long living cached objects. Remember that at the end of the day the whole WebAPI (so far) runs on the web stack, this means the application can be recycled unexpectedly based on different factors.

Related

Share my dbContext with all my repository/service class?

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.

How to serve up a properly scoped DbContext in an NTier Api that is application-type agnostic?

I am writing a class library using EF Code First that I expect to be used in all types of applications including web, console, and desktop. Currently my API, which exists only as a web API, uses a standard factory pattern to return a DbContext instance scoped to HttpContext.
The method call looks like this:
EntityDbContextFactory<MyDbContext>.GetInstance();
And the implementation:
public class EntityDbContextFactory<TContext>
where TContext : class, IDisposable, new()
{
private static TContext _dbContext;
public static TContext GetInstance()
{
TContext context;
if (HttpContext.Current != null)
{
var objectContextKey = HttpContext.Current.GetHashCode().ToString("x") +
typeof(TContext).GetHashCode().ToString(CultureInfo.InvariantCulture);
if (!HttpContext.Current.Items.Contains(objectContextKey))
{
context = new TContext();
HttpContext.Current.Items.Add(objectContextKey, context);
}
else
{
context = HttpContext.Current.Items[objectContextKey] as TContext;
}
}
else
{
if (_dbContext == null)
{
_dbContext = new TContext();
context = _dbContext;
}
else
{
context = _dbContext;
}
}
return context;
}
}
Very standard stuff, I've seen this in many places online. I need to supply this kind of scoping manually because I can't guarantee programmers using my API are using a DI container, and even if I could, I don't want my repository tracking state (by injecting the DbContext into the repository constructor as commonly seen), nor do I want people calling my services to have any care about my data access method.
Now the problem becomes, how do I expand this out to encompass console and desktop apps? If I use the above in those apps, I have to reference System.Web, which doesn't seem appropriate at all.
Standard singleton patterns could work for desktop and console, but then if either app is open a long time, the entities cached in the context could very well become stale. So I certainly don't want to do full singleton for lifetime of apps approach, but then how to you scope them? I would like to be able to still be efficient with my DbContexts creation (and service and repository creation) and not create new ones every single time if possible. And not rely on external libraries.
Well it seems silly, but I am almost already doing that. As mentioned by the link in the comments, you don't want to keep a context alive at all in a console or desktop app because those apps are stateful and the context can become stale very quickly. So for those instances, you should be using a new instance every time it's called for.
However, in a web application which has no state, it is appropriate and common practice to scope the DbContext to the HttpContext (Per Web Request). Each request may contain multiple service calls and it makes sense to only use one dbcontext instance per request. Almost think of it like a transaction. There are numerous examples from Microsoft themselves showing this.
However, my approach has an added benefit. Most examples online you see of NTier using this approach store the dbcontext as a member of the repository class. This makes your NTier API stateful and then as required now (since the dbcontext is not threadsafe) you must scope your repository and service classes to one instance per request. But with my approach, you can scope your service and repository classes as singletons, which is more efficient.
Common scenario seen online:
public class UserRepository
{
private MyDbContext _myDbContext;
public UserRepository(MyDbContext myDbContext)
{
_myDbContext = myDbContext;
}
}
DbContext is not thread safe, therefore every layer in your ntier app (commonly dboncontext -> repository -> service) must be scoped to per http request in your DI container.
My approach:
public class UserRepository<TbContext>
{
private DbContext Context
{
get { return EntityContextFactory<TDbContext>.Current(); }
}
}
Using the above code in the question for the factory class. Now my services and repositories can be singletons and regardless of what kind of app I am using my API in, it will handle the dbcontext lifetime as appropriate. The only change that is needed, is if HttpContext is null, return a new instance and don't store it as a private member:
public class EntityContextFactory<TContext>
where TContext: class, IDisposable, new()
{
private static TContext _dbContext;
public static TContext Current()
{
TContext context = null;
if (HttpContext.Current != null)
{
string objectContextKey = HttpContext.Current.GetHashCode().ToString("x") +
typeof (TContext).GetHashCode().ToString(CultureInfo.InvariantCulture);
if (HttpContext.Current.Items.Contains(objectContextKey) == false)
{
context = new TContext();
HttpContext.Current.Items.Add(objectContextKey, context);
}
else
{
context = HttpContext.Current.Items[objectContextKey] as TContext;
}
}
else
{
context = new TContext();
}
return context;
}
}

Repository and UoW pattern with service layer

I'm using Repository and UoW pattern. My services look like this:
public class MyService : IService
{
private readonly IUnitOfWork<MyContext> unitOfWork;
private readonly IMyRepository myRepository;
public MyService(IUnitOfWork<MyContext> unitOfWork, IMyRepository myRepository)
{
this.unitOfWork = unitOfWork;
this.myRepository = myRepository;
}
//Methods...
}
Within services, I need to use other entities (for example to check for rights, etc).
Is it recommended to use the relevant repositories in the service or use the services directly?
Also, for each user we have rights (boolean) for each CRUD action. These rights are stored in the database.
Should checking of rights be done at the controller level or at the service level?
My golden rule is:
When you get business logic in your UI create a service, otherwise use
the repository directly.
So if you have this code in the UI:
var user = repos.Get(1);
user.FirstName = txtFirstName.Text;
repos.Save(user);
You are fine in my opinion. But if you instead have something like:
var user = userRepository.Get(1);
var accessChecker = authorizationRepository.GetForUser(id);
if (!accessChecker.MaySendEmail(user))
throw new SecurityException("You may not send emails");
var emailSender = new EmailSenderService();
emailSender.Send(user, txtDestination.Text, txtMessage.Text);
repos.Save(user);
It's likely that you should use a service instead.
Don't use your UoW to just wrap your database context. Since all your repositories are directly dependent of a given context (more or less, ofc), your repositories can be included in the UoW. Something along the lines of:
public interface IUnitOfWork<TContext> : IDisposable { }
public abstract class UnitOfWork<TContext> : IUnitOfWork<TContext> {
private readonly TContext _context;
protected TContext Context { get{ return _context; } }
protected UnitOfWork(TContext context){
_context = context;
}
}
public interface IMyDbUnitOfWork : IUnitOfWork<MyContext>{
public ICarRepository Cars { get; }
public IOwnerRepository Owners { get; }
}
public class MyDbUnitOfWork : UnitOfWork<MyContext>, IMyDbUnitOfWork{
public MyDbUnitOfWork():base(new MyContext()){}
private ICarRepository _cars;
public ICarRepository Cars {
get{
return _cars ?? (_cars = new CarRepository(Context));
}
}
private ICarRepository _owners;
public IOwnerRepository Owners {
get{
return _owners ?? (_owners = new OwnerRepository(Context));
}
}
}
public class MyService : IService
{
private readonly IMyDbUnitOfWork _unitOfWork;
public MyService(IMyDbUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
//Methods...
}
Obviously you can create this more or less generic, but I believe this should be enough to pass my point.
As a note, and since I normally use IoC frameworks, my services receive an IUnitOfWorkFactory because of the diferent lifestyles.
For the permissions question, it really depends how much control you want to have and how user friendly you want your application to be. Normally is a mix of both. Your application should know if your user has access to the screen but also if you must disable buttons accordingly. Since you also must prevent that, if by any reason, the user can invoke your service method, you can't allow it.
To solve this problem I don't filter by CRUD actions but by Service actions instead, intercepting every service invocation, which makes it easy to map my permissions to the user interface since normally is a 1 to 1 relation between button action and service action.
I think using repositories is just fine. I wouldn't invent a service layer for each of the repos.
Repository is used for abstracting the data access and service layer is to encapsulate business logic, however with recent trend , I find this overkill. Having service layer is fine if they act as controllers but don't try to map one to one to each entity or repo.
I typically use services from the UI and those services in turn use the repositories. I also find it useful to have some domain objects that encapsulate reusable logic in the services.
I do this so that rather than services calling each other and getting circular references, services use a common domain object instead. This avoids circular references and people copying and pasting the same code all over the place.This domain object may then use the repositories if necessary.

Using Fluent NHibernate with a Data Access Layer

First a little background: I have a solution with the following 3 projects in it:
MVC Project (User facing website)
API Project (business logic project)
Data Access Project (project where NHibernate lives)
I have the Fluent mappings in the DA layer, and (for now) I build the Hibernate SessionFactory in the GLobal.asax of the MVC site. This is not ideal as I want to have NHibernate completely contained in the DA layer and have the MVC app only communicate with the API layer. Also, I want to build the SessionFactory only once as it is an expensive operation.
To make things more complicated I have an inheritance structure like so:
User object in API layer inherits from
User data object in DA layer inherits from
Data object in DA layer.
Data object is responsible for saving the object to the database as the saving function is the same across all objects and I do not want to repeat code. The problem I am having is how do I save the User object to the database from inside the Data object class while using a SessionFactory that I instantiated when the user logged into the website and can persist through out their session.
If anything is not explained clearly please let me know.
One way to do that would be using the DI pattern, with e.g. Unity.
Implement your data object having a constructor which takes for example an IRepository interface. The implementation of this interface handles the nHibernate session factory...
Your data object could also be generic where T is one for example User data object. Then you implement a methods in data object to e.g. save, update, delete T with the injected IRepository
pseudo code for a data object
public interface IEntity
{
}
public interface IRepository
{
ISession Session { get; }
}
public class DataObjectBase<T> where T : IEntity
{
private IRepository Repository { get; set; }
public DataObjectBase(IRepository repository)
{
this.Repository = repository;
}
public T Get(int id)
{
return Repository.Session.Get<T>(id);
}
public void Save(T value)
{
Repository.Session.Save(value);
}
public void Update(T value)
{
Repository.Session.Update(value);
}
public void Delete(T value)
{
Repository.Session.Delete(value);
}
public IQueryable<T> Query()
{
return Repository.Session.Query<T>();
}
}
Implementation of your specific data object
public class ADataObject : IEntity
{
public int Id { get; set; }
// [...]
}
Implementation of your data context for the data object
public class ADataObjectContext : DataObjectBase<ADataObject>
{
public ADataObjectContext(IRepository repository)
: base(repository)
{
}
}
A simple example test using Unity
public class Test
{
public void Run()
{
IUnityContainer myContainer = new UnityContainer();
myContainer.RegisterType<IRepository, NHibernateRepository>();
var ctx = myContainer.Resolve<ADataObjectContext>();
var obj = ctx.Query().Where(p => p.Id == 2);
}
}
Of cause you would have to implement the NHibernateRespository to do whatever you want it to.
The UnityContainer initialization should be done within your global.asax within the MVC project. You can also configure Unity via web.config.
The NHibernateRespository should actually be a singleton. This can either be implemented by you, or you simply use the Unity functionality to instantiate your type as singleton. The new ContainerControlledLifetimeManager() does exactly that.
Instead of exposing the session as a property you can of cause expose a method which opens a new session. Or you implement a Begin and End unit of work, which is common practice in web environments...
Other links for a normal repository pattern, and unit of work, unity... or simply search on Google for nhibernate repository pattern
http://slynetblog.blogspot.de/2011/11/in-spite-of-common-now-approach-of.html
http://blog.bobcravens.com/2010/07/using-nhibernate-in-asp-net-mvc/
http://msdn.microsoft.com/en-us/library/dd203101.aspx
You can use this options:
Using AOP: when a function is called in API layer, AOP creates a session and passes the value parameters in methods or constructors to DA layer.
From MVC project to DA layer, it passes a session to DA layer with parameters in method o constructors, through all the layers.
Thinks the session is always associated with the interface layer.

Where to keep dictionaries in app using Dependency Injection

I have a legacy code, and I have a problem with reconstructor it.
At start of my application I load from WCF to property on App (this is SL application) list of users.
Then every control (for sending emails, view calendar and assigning tasks) use this property as
(App.Current as App).Users
Now, I'm trying to create Unit Test for one of controls that use this lists, and I'm stuck.
Should I make a Constructor Injection(I'm using Unity) with App as parameter? Or maybe introduce some class to hold this list?
Updated with OP's implementation as the pseudocode was incomplete.
I propose create an interface for all your application services
Inject IApplicationService to your modules.
You can use this interface for all the services the application provides(probably you will need more). Mock the interface for the unit tests
OP's implemantation
public interface IApplicationService
{
List<User> Users{get;set;}
}
public class ApplicationService : IApplicationService
{
public List<User> Users
{
get { return (App.Current as App).Users; }
set { (App.Current as App).Users = value; }
}
}
public partial class MainWindow : UserControl
{
readonly IApplicationService _applicationService
public MainWindow(IApplicationService applicationService)
{
_applicationService=applicationService;
}
}
I would create a wrapper class that will expose the list of users. In production code this class will just be a wrapper around your App.Current property and it can be injected in the constructor trough Unity.
In your Unit Tests you can easily mock the App parameter and pass it when constructing a new SUT.
Something like:
public interface IUserList
{
List<User> Users { get; }
}
public class SUT
{
private IUserList UserList { get; set; }
public SUT(IUserList userList)
{
this.UserList = userList;
}
}
public class AppUserList : IUserList
{
public List<User> Users
{
get
{
return ((App)App.Current).Users;
}
}
}
For Silverlight there is an extension model called Application Extension Services.
For infrastructure purposes that might be a better alternative than adding properties to your app class and casting App.Currentback and forth.
Downside of that model is the creation of a singleton you would have to initialize for your unit tests. It would also hide the dependency on Users in your consuming classes.
Your users seem to be just data. Making that data an ambient context which can be accessed and edited everywhere in your application will bite you. You don't know who does what with that data and when he does it. This is like a session state.
So making the dependency on your data explicit would be a first step to be able to track abuse of that data.
If it makes sense to you to create a "data holder object" that has a property for Users or directly inject that data into your consumers is up to you. If there is more data than just Usersit is tempting to put all of them into the same central data store object, even if your specific consumers don't need them.
Jimmy's answer is great, but can be provide quite a bit, and some errors fixed. Differences are explained at the bottom below the code/instructions:
Create a public interface: IUserService
public interface IUserService
{
// Implemented functionality as methods where possible for better
// extendability (like IoC)
IEnumerable<User> Users();
// Add any other user service stuff as you see fit.
void AddUser(User user);
}
Write a UserService that implements IUserService
public class UserService : IUserService
{
// If you need DI for this service, follow the same pattern of using
// fields and controller injection. I left examples in comment below.
// private readonly IRepository _repository;
// Constructor is unnecessary if you do not need DI example.
public UserService(/* IRepository repository */)
{
// _repository = repository;
}
// Methods
public IEnumerable<User> Users()
{
return ((App)App.Current).Users;
}
public void AddUser(User user)
{
((App)App.Current).Users.Add(user);
}
}
Inject IUserService into classes via their Constructor
In this case your MainWindow as an example:
public partial class MainWindow : UserControl
{
private readonly IUserService _userService;
public MainWindow(IUserService userService)
{
_userService = userService;
}
// Example method consuming the service
public IEnumerable<User> GetUsers()
{
return _userService.Users();
}
}
Differences:
Separate your User Services from a central Application Service
Better modularity. In addition I use an IApplicationService for more central/global data like Api Keys, Timeouts, cleanup, DB prepping, etc.
Return IEnumerable<T> instead of List<T>
This is just a golden rule of thumb for keeping things dry and not imposing hard instantiations on your consuming classes. Refactoring is easier/safer, and your code more extensible.
Use methods instead of properties
This is preference, but I think it smart in a service layer to use methods where possible so that you can introduce filters and overloads or continue to use dependency injection - for example, you could add GetUsers(string lastName), GetUsers(string lastName, string firstName) and maintain a clean interface for your consuming classes.
Cast App.Current without the as keyword
This is a good practice because using the as keyword means when the cast fails it will return null, rather than throw an exception. I prefer the exception because 99% of the time, if your cast fails, your next operations will too. :)
Enjoy!

Categories