I have created a struct on validating models on Business Layer which is based on Steven's answer.
It is working well but something confuses my mind. I inject UserService in CreateUserValidator to able to use GetUser method. This means I call validator in UserService and create a new UserService instance to check whether user exist.
UserService -> [ValidateUser -> new UserService().GetUser()]
It works but seems to be a very bad design. But I have to use that method.
Could you please let me know how I can solve this problem, or Shouldn't I worry about it?
public class CreateUser
{
public string Name { get; set; }
public string Email { get; set; }
}
public sealed class CreateUserValidator : Validator<CreateUser>
{
private IUserService _userService;
public CreateUserValidator(IUserService userService)
{
_userService = userService;
}
protected override IEnumerable<ValidationResult> Validate(
CreateUser entity)
{
var user = _userService.GetUserByEmail(entity.Email);
if (user != null)
{
yield return new ValidationResult("Email", "Email address is already exist!");
}
}
}
UserService.cs
public partial class UserService : IUserService
{
IGenericUnitofWork _uow = null;
private readonly IValidationProvider _validationProvider;
public UserService(IGenericUnitofWork uow, IValidationProvider validationProvider)
{
_uow = uow;
_validationProvider = validationProvider;
}
public User CreateUser(CreateUser createUser)
{
this._validationProvider.Validate(createUser);
var user = new User()
{
Email = createUser.Email,
Name = createUser.Name,
};
_uow.Repository<User>().Insert(User);
_uow.SaveChanges();
return user;
}
public User GetUser(string email)
{
var user = _uow.Repository<User>().Where(m => m.Email == email).FirstOrDefault();
return user;
}
}
You dependency graph is cyclic. As described in section 6.3 of Dependency Injection in .NET second edition, dependency cycles are often caused by Single Responsibility Principle violations, as is the case in your design.
The problem is that UserService has too many responsibilities: Creating a user is a different responsibility than getting a user. Creating a user can become a very complex use case, as the validation logic hints at, while getting a user is something typically quite simple. It would therefore be beneficial to split UserService into multiple smaller classes. This would allow the validator to depend on the service that allows retrieving the user by its mail address, while the 'create user' service can depend on the validator.
To take it even one step further, you might want to remove validation from the 'create user' service completely. Validation is a cross-cutting concern, and mixing it with the class that contains the business logic, makes such class harder to maintain.
A design that might benefit you is one where you place all state changing business actions behind a common abstraction, as described here.
Related
I've got a scoped service, that needs to instantiate with user specific variables.
builder.Services.AddScoped<IUserService, UserService>();
UserService currently has a logger as it's constructor.
I'm currently doing the following through a factory, in a hacky way:
public class UserServiceFactory
{
private readonly ServiceProvider _sp;
private readonly DbContext _db;
public UserServiceFactory(ServiceProvider sp, DbContext db) { _sp = sp; _db = db; }
public async Task<IUserService> GetUserServiceForUserAsync(Guid userId)
{
var (apiKey, apiSecret) = await _db.FetchApiKeyAndSecretAsync(userId);
var userService = _sp.GetRequiredService<IUserService>();
userService.InitUser(apiKey, apiSecret);
return userService;
}
}
I'm running into the following problems:
I can't use builder.Services.AddScoped<IUserService, UserService>(); with string parameters, because as soon as it attempts to register in DI, it can't resolve the string parameters in the constructor, even though the only place I'm going to be initializing it will be in the factory, and I'll be providing said string parameters.
If I don't use builder.Services.AddScoped<IUserService, UserService>();, I'd need to use Activator.CreateInstance<UserService>(...), which ties a concrete implementation to this class which is not ideal. In addition, I can't track said UserService for disposal when the scope gets disposed.
It was suggested that I register a Func<> to return a user service. If I do this, I don't believe it will be a scoped service, and thus not be disposed of properly when the scope is destroyed.
The implementation of UserService is essentially an an HTTP Client, that will make requests with an apiKey and apiSecret of the IdentityUser. I'd like it to exist for the duration of the scope (In the case of asp.net core, the request, or in the case of being called from a Quartz job, the duration of the job), and then dispose afterwards.
UserService contains about 20 various methods, such as FetchAccountAsync, BuyItemAsync(itemId, quantity), SellItemAsync(itemId), which should make requests using the initialized httpclient. I'd like to avoid trying to initialize the apiKey/apiSecret in each method, because this will add a level of synchronization that I don't feel is needed. HttpClient is by default multithreaded, so my methods are fairly pain free:
Task BuyItemAsync(string itemId, int quantity)
{
var res = await _httpClient.GetAsync($"{_baseUrl}/buy?itemId={itemId}&qty={quantity}");
res.EnsureSuccessStatusCode();
}
How can I initialize my UserService with these apiKeys, apiSecrets, to be used in a scoped manner?
NOTE: Some of these details I've added based on your comments. Some of these may appear a little contrived because I don't know your full logic, context, or needs.
Design
I suggest the following
Remove the factory.
Go ahead and use builder.Services.AddScoped<IUserService, UserService>();.
Change the constructor of UserService to accept an ISecurityContext that will provide the API key and secret. This context will also be registered with AddScoped.
Have the UserService use the ISecurityContext at runtime, and remove any properties/parameters for API key and secret (if you had them).
Have the SecurityService use an IUserProvider at runtime, and remove any properties/parameters for user ID (if you had them).
This means no runtime data needs to be injected, no hacky method to expose, no factory, and no injecting the service provider.
At startup register the interfaces. It is important that they be scoped. They are going to share the lifetime, which will be short.
...
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<ISecurityContext, SecurityContext>();
builder.Services.AddScoped<IUserProvider, UserProvider>();
Then implement the classes and a Result that can return all the contextual data.
public class Result
{
public Result(string apiKey, string apiSecret, Guid userId)
{
ApiKey = apiKey;
ApiSecret = apiSecret;
UserId = userId;
}
public string ApiKey { get; }
public string ApiSecret { get; }
public Guid UserId { get; }
}
public interface IUserProvider
{
Guid GetUserId();
}
public class UserProvider : IUserProvider
{
public async Task<Guid> GetUserId() => IdentityUser.GetUserId());
}
public interface ISecurityContext
{
Task<Result> GetApiKeyAndSecretAsync();
}
public class SecurityContext : ISecurityContext
{
private readonly DbContext _db;
private readonly IUserProvider userProvider;
// safe because this SecurityContext will be scoped!
private Result _result;
public SecurityContext(DbContext db, IUserProvider userProvider)
{
_db = db;
_userProvider = userProvider;
}
public async Task<Result> GetApiKeyAndSecretAsync()
{
if (_result != null) return _result;
var userId = _userProvider.GetUsedId();
var (apiKey, apiSecret) = await _db.FetchApiKeyAndSecretAsync(userId);
return _result = new Result(apiKey, apiSecret, userId);
}
}
public interface IUserService
{
Task DoWhatever();
}
public class UserService : IUserService
{
private readonly ISecurityContext _context;
public UserService(ISecurityContext context) => _context = context;
public async Task DoWhatever()
{
// use the security context
var result = await _context.GetApiKeyAndSecretAsync();
// use the result; e.g. pass the key/secret/user ID
// on to an HttpClient, RestClient, etc.
...
}
...
}
Usage
Using an IUserService means injecting that into your Quartz.NET job, a message handler, a web controller... wherever. In each case you may realize that one single implementation of any of these interfaces is not enough. That's OK. There are ways in dependency injection to fix that (e.g. named resolutions of multiple different concrete implementations), but I leave that to you.
Here's an example usage for a web controller.
public class MyController
{
private readonly IUserService _userService;
public MyController(IUserService userService, ...)
{
_userService = userService;
...
}
[HttpGet]
public async Task<IActionResult> GetStuff(...)
{
// gets the key and secret first time
await _userService.DoWhatever();
// uses cached versions of key, secret, guid across
// calls of _userService methods within scope
var someResult = await _userService.GetSomethingElse();
...
}
Commentary
This design has a few advantages
Security details are encapsulated behind an abstraction and not mixed into the UserService
The whole thing is more testable because the security details can be mocked when testing the UserService.
Key and secret are cached once within the scope and can be reused across methods in UserService that are invoked while in the same scope.
As #NKosi said in the comments, mixing runtime data at construction time is an anti-pattern. The link they referenced, Dependency Injection Code Smell: Injecting runtime data into components, is a good read and goes into more depth.
As you add more runtime data, you can expand the properties in Result and logic in SecurityContext or you can inject more context-like objects into UserService returning their own result-like instances.
There is a placeholder pattern that I have found useful here.
STARTUP CODE
Define dependencies in your application startup code, something like the following. Note that .NET does not allow you to run async processing in the factory method for IUserService:
app.UseMiddleware<DependencySetupMiddleware>();
services.AddSingleton(new MyDatabase());
services.AddScoped<UserServiceHolder>();
services.AddScoped<IUserService>(ctx =>
{
return ctx.GetRequiredService<UserServiceHolder>().UserService;
});
The holder class just looks like this:
public class UserServiceHolder {
public IUserService UserService { get; set; }
}
MIDDLEWARE CODE
The async processing can be done in a small middleware class. For the HTTP case you would do it like this, assuming that you get the User Id after authentication. Note that dependencies cannot be added to the .NET container at runtime, but you can update the holder object:
public class DependencySetupMiddleware
public DependencySetupMiddleware(RequestDelegate next) {
}
public async Task Invoke(HttpContext context, MyDatabase db) {
var userId = context.User.Claims.First(c => c.Type == "UserId")
var (apiKey, apiSecret) = await db.FetchApiKeyAndSecretAsync(userId);
var userService = new UserService(apiKey, apiSecret)
context.GetRequiredService<UserServiceHolder>().UserService = userService;
await next();
}
}
For Quartz you would have a similar middleware class - a Job Factory, which reads the job's user ID rather than using claims or the HTTP context.
BUSINESS LOGIC
With this code in place you can inject an IUserService into your business logic and forget about the holder class:
class MyController {
public MyController(IUserService userService) {
}
}
I think you might already have an answer here, but let me give you a working example. Here's my assumption:
I want to have an instance of a class that has all the things about the user available.
Here's the approach I used for PopForums.
Step 1: You're using some kind of built-in ASP.NET authentication, probably cookies or something external. I won't cover that here, because there are many ways to do it, but look at HttpContext.SignInAsync() for more. The important part is to use a name or identifier that will be put into the token it reads back in the next step.
Step 2: Use middleware to get your user and make it stick. You'll start with a ClaimsIdentity when you use HttpContext.AuthenticateAsync(schemeName). For example:
public async Task InvokeAsync(HttpContext context, IUserService userService)
{
var authResult = await context.AuthenticateAsync(schemeNameUsedFromSignIn);
var identity = authResult?.Principal?.Identity as ClaimsIdentity;
if (identity != null)
{
var user = userService.GetUserByName(identity.Name);
if (user != null)
{
// add claims to the identity if you want
// then stash your user object in the Items collection, which lasts the duration of the request
context.Items["TheUser"] = user;
context.User = new ClaimsPrincipal(identity);
}
}
await _next.Invoke(context);
Step 3: Enable getting the user anywhere you want by pulling it out of the context of the request, but isolate it to an interface so there are no hard dependencies. Example:
public interface IUserRetrievalShim
{
User GetUser();
}
public class UserRetrievalShim : IUserRetrievalShim
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserRetrievalShim(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public User GetUser()
{
var user = _httpContextAccessor.HttpContext?.Items["TheUser"] as User;
return user;
}
}
What I like about this approach is that any classes up and down the dependency chain can be mocked out and unit tested without all of the HttpContext references. IHttpContextAccessor does a great job isolating it, and if it's not available, you'll get a null. And in this case, you're getting your user object, not the one tied to ASP.NET. You can still check HttpContext.User != null if you want, but this similarly will be null if there's no authenticated user. I only do the above with claims because maybe other app areas may want it.
Step 4: In your controllers, service classes or anything in between, inject IUserRetrievalShim and call its GetUser() method to get the user.
The bottom line here is that dependency injection is not the place to make the user stuff contextual. DI is purely setup and configuration, not run-time context. Use your UserService where ever you want, and combined with this shim, you can pass its ID or whatever to those service methods. You should not expect the service to be contextual out of the box by way of injection.
With that said, your User objects (not to be confused with HttpContext.User) can be composed of whatever you want, so long as you're OK with whatever the cost is to fetch that information and hydrate the object.
My current implementation of passing UserID in my application is through the constructor.
i.e. SomeObject s = new SomeObject(userID)
Where in there is a code behind that does things based on the userID. The userID is further keep tracked by adding another property named "CurrentUser", however this seems to be a dirty solution as I have to implement it to all ViewModels and it seems to violate the "DRY" concept.
The second approach I have in mind is creating a public static variable on my MainWindowViewModel where all my other models can refer to it as MainWindowViewModel.CurrentUser.
Is one of the two approach the correct way to do this or is there a better approach that i don't know about?
You need to carefully analyze up front what you want to achieve with your application. Are you happy with there only ever being one selected client? Or will you need to have multiple clients being viewed or edited at a time (i.e. you have an MDI style app)?
Going with the single client approach is easy, you can implement the global property bag as already mentioned in other answers. But I will advise caution: if you build your app on the assumption there will only ever be one selected client it becomes a real PITA to try to refactor to make it multi-client capable. Using a centralized property bag or "session service" like this is indeed decoupling state from the VM, but the centralized service can still turn into a monstrosity over time and you build up too much dependence on it.
If you do want to go the multi-client route, then you are on the right track - but instead of passing a client identifier in on the constructor, pass (inject) the entire client data object. The chances are that you already have most of the client details available from the piece of UI that invokes the client oriented VM, so pass it in and save having to make another trip to your database to get the details.
Don't tie a current user to a ViewModel. I typically opt for a SessionService of some kind. If you're using Dependency Injection (DI), register a singleton of an ISessionService and concrete implementation. If your not using DI, then just have your app start create a singleton, like a SessionService.Current. Then you can put any items you need in here. Then each ViewModel can ask for the SessionService.Current.User and they have it. Your ViewModels shouldn't know about each other, but they can know about services. This keeps it DRY and loosely coupled, especially if you only access these session variables using the interface of an ISessionService and not the concrete implementation. This allows you to mock one up very easily without changing any ViewModel code.
What you have here is the problem of Communication between ViewModels. There are a number of solutions but my fave is the Mediator Pattern:
using System;
namespace UnitTestProject2
{
public class GetDataViewModel
{
IMediator mediator;
public GetDataViewModel(IMediator mediator)
{
this.mediator = mediator;
this.mediator.ListenFor("LoggedIn", LoggedIn);
}
protected string UserId;
protected void LoggedIn(Object sender, EventArgs e)
{
UserId = sender.ToString();
}
}
public class LoginViewModel
{
IMediator mediator;
public LoginViewModel(IMediator mediator)
{
this.mediator = mediator;
}
public string UserId { get; set; }
public void Login(string userid)
{
this.UserId = userid;
this.mediator.RaiseEvent("LoggedIn", this.UserId);
}
}
public interface IMediator
{
public void ListenFor(string eventName, EventHandler action );
public void RaiseEvent(string eventName, object data);
}
}
I Haven't implemented the Mediator here, because it can get quite involved and there are a number of packages available. but you can see the idea from my simple interface. Essentially the Mediator provides a Global list of EventHandlers which any Viewmodel can call or add to. You still have the problem of where to store the event names. Its nice to have these in enums, but that gives you a coupling problem. (a problem I usually ignore)
Alternatively you can have a Controller or (MasterViewModel if you love MVVM)
using System;
namespace UnitTestProject3
{
public class GetDataViewModel
{
protected string UserId;
public void LoggedIn(Object sender, EventArgs e)
{
UserId = sender.ToString();
}
}
public class LoginViewModel
{
public EventHandler OnLogin;
public string UserId { get; set; }
public void Login(string userid)
{
this.UserId = userid;
if (this.OnLogin != null)
{
this.OnLogin(this.UserId, null);
}
}
}
public class Controller // or MasterViewModel
{
public void SetUp()
{
GetDataViewModel vm1 = new GetDataViewModel();
LoginViewModel vm2 = new LoginViewModel();
vm2.OnLogin += vm1.LoggedIn;
//wire up to views and display
}
}
}
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.
I have a small webapp that uses EntityFramework to store stuff via repositories into the database.
What I've done so far (based on all the tutorials I read) is create a repository where I need it, as shown below:
In CustomMembershipProvider:
public CustomMembershipProvider()
{
_userRepository = new UserRepository(new TenantApplicationContext());
}
In my RegisterController:
public TenantRepository TenantRepository { get; set; }
public UserRepository UserRepository { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if (MembershipService == null) { MembershipService = new AccountMembershipService(); }
if (TenantRepository == null) { TenantRepository = new TenantRepository(TenantApplicationContext); }
if (UserRepository == null) { UserRepository = new UserRepository(TenantApplicationContext); }
base.Initialize(requestContext);
}
The point is, that I instantiate the UserRepository twice. This becomes a problem when I create a User in one instance, and try to retrieve it in the other instance, and I did not call SaveChanges in between.
The problem lies here:
// Snippet from the register controller class
if (!UserRepository.Exists(model.AccountableEmailAddress))
{
// 1 - Create the user via a custom MembershipProvider
// Note, the CustomMembershipProvider has it's own instance of UserRepository
var createStatus = MembershipService.CreateUser(
model.AccountableUser,
model.Password,
model.AccountableEmailAddress);
if (createStatus == MembershipCreateStatus.Success)
{
// Left out irrelevant code
AdministerUserAndTenant(tenant.Name, model.AccountableEmailAddress);
}
}
private void AdministerUserAndTenant(string tenantName, string emailAddress)
{
// 2 - Try to retrieve the user from a different (!) instance of UserRepository
var user = UserRepository.GetUser(emailAddress);
var tenant = TenantRepository.GetTenantByName(tenantName);
tenant.Users.Add(user);
TenantApplicationContext.SaveChanges();
}
I hope you can still follow, tried to leave out unnecessary parts.
What is the best way to deal with issues like this?
PS: I'm not very fond of the Singleton pattern, so if possible don't go there :).
When exactly does it become a problem? Cause that's where the answer lies. Classes that should know of each other's unsaved changes should use the same repository instance. Since they are probably related, you'll manage passing a reference between them.
If there's reason why all of your application should have one single repository, use 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!