Fixing cyclic dependencies by adding a new interface - c#

Below is some code from a book which shows how cyclic dependencies:
public interface IAuditTrailAppender {
void Append(Entity changedEntity);
}
public class SqlAuditTrailAppender : IAuditTrailAppender {
private readonly IUserContext userContext;
private readonly CommerceContext context;
private readonly ITimeProvider timeProvider;
public SqlAuditTrailAppender(IUserContext userContext, CommerceContext context, ITimeProvider timeProvider) {
this.userContext = userContext;
this.context = context;
this.timeProvider = timeProvider;
}
public void Append(Entity changedEntity) {
AuditEntry entry = new AuditEntry {
UserId = this.userContext.CurrentUser.Id,
TimeOfChange = this.timeProvider.Now,
EntityId = entity.Id,
EntityType = entity.GetType().Name
};
this.context.AuditEntries.Add(entry);
}
}
public class AspNetUserContextAdapter : IUserContext {
private static HttpContextAccessor Accessor = new HttpContextAccessor();
private readonly IUserRepository repository;
public AspNetUserContextAdapter(IUserRepository repository) {
this.repository = repository;
}
public User CurrentUser {
get {
var user = Accessor.HttpContext.User;
string userName = user.Identity.Name;
return this.repository.GetByName(userName);
}
}
}
public class SqlUserRepository : IUserRepository {
public SqlUserRepository(CommerceContext context, IAuditTrailAppender appender) {
this.appender = appender;
this.context = context;
}
public void Update(User user) {
this.appender.Append(user);
}
public User GetById(Guid id) { ... }
public User GetByName(string name) { ... } // <--- used by CurrentUser property of AspNetUserContextAdapter
}
You can see cyclic dependencies exist as the picture below shows:
The author says "these kind of dependency cycles are typically caused by single-responsibility principle (SRP) violation. To fix it, the author adds a new interface IUserByNameRetriever:
public interface IUserByNameRetriever {
User GetByName(string name);
}
public class SqlUserByNameRetriever : IUserByNameRetriever {
public SqlUserByNameRetriever(CommerceContext context) {
this.context = context;
}
public User GetByName(string name) { ... }
}
public class SqlUserRepository : IUserRepository {
public SqlUserRepository(CommerceContext context, IAuditTrailAppender appender) {
this.appender = appender;
this.context = context;
}
public void Update(User user) {
this.appender.Append(user);
}
public User GetById(Guid id) { ... }
// public User GetByName(string name) { ... } don't need this method anymore
}
public class AspNetUserContextAdapter : IUserContext {
private static HttpContextAccessor Accessor = new HttpContextAccessor();
private readonly IUserByNameRetriever retriever;
public AspNetUserContextAdapter(IUserByNameRetriever retriever) {
this.retriever = retriever;
}
public User CurrentUser {
get {
var user = Accessor.HttpContext.User;
string userName = user.Identity.Name;
return this.retriever.GetByName(userName);
}
}
}
I can understand how the introduce of IAuditTrailAppender stops the dependency cycles, but I feel like it is just a workaround. I don't understand why the author said this dependency cycle is caused by SRP violation. Because if you look at SqlUserRepository, its GetByName method is a nature to have it in the class just like GetById method (consumers can search a user by its id, and of course, it is nature for consumers to search a user by name), I can't see why having GetByName method in SqlUserRepository is a SRP violation?

It's pretty hard to see, but the problem is with the definition of IUserRepository.
The question is: Is this object supposed to be used across multiple requests, to provide the user repository to the system as a whole, or is it created for each request to provide a user repository to that specific requests. Both types of repository are used in practice, but the original example seems to want it to be both at the same time, which is indeed an SRP problem.
If it's intended to be used across multiple requests, then it doesn't make sense that it logs its audit trail in the context of one specific user. A user repository's audit logger should not then require a IUserContext. This is not what the author has decided.
The author has decided to go the other way, and construct a IUserRepository for each request, so that all of its operations are performed in the context of the specific user that performs them -- probably a good idea. That means that the audit logger is fine, but it doesn't make sense that you would require an IUserRepository to create a user context in the first place. You can't perform any of the dangerous user repository methods without a user context to audit them against.
So you need a way to bootstrap a user context without a full repository. The narrower IUserByNameRetriever class fills this role. Either it cannot perform any sensitive operations that require logging, or it creates adequate logs through some other path that doesn't require a user-tied IAuditTrailAppender.

Related

How to get multiple implementations of the same service interface each in separate scopes

I have an asp.net core 2.2 web application. There is an Interface which is implemented by multiple classes.
services.AddTransient<IHandler, HandlerA>();
services.AddTransient<IHandler, HandlerB>();
Each implementation of IHandler injects scoped EF DbContext and if the same DbContext is shared between different threads, then periodically a floating exception will occur when trying to access the same entity. So I get each handler in a separate scope.
using (var scope = _serviceProvider.CreateScope())
{
var handlers = scope.ServiceProvider.GetServices<IHandler>();
await Task.WhenAll(handlers.Select(async h =>
{
using var internalScope = scope.ServiceProvider.CreateScope();
var handler = internalScope.ServiceProvider
.GetServices<IHandler>()
.First(f => f.Name == h.Name);
await handler.Handle(cancellationToken);
}));
}
This solution seems to work, but I'm not sure if it's optimal. Maybe there is a better way to get multiple implementations of the same service interface in separate scopes?
You do not need to access same entity using different DBContext.
Define services and inject in controllers explained above link. Each service will access DB using same db context through Manager Classes.
You can use service this way as you seeking best practices. And use dependency injection to access the services from controllers.
Define Service, Domain classes and interfaces.
public class Service : Attribute
{
}
//Domain Classes
public class Entity
{
string str {get; set;}
int numb {get; set;}
}
//DBContext
public class DbContext : IdentityDbContext
{
public DbContext(DbContextOptions<DbContext> options)
: base(options)
{
}
public DbSet<Entity> Entities { set; get; }
}
//Interfaces
interface IHandler
{
string method1(Entity entity);
int method2();
}
Implement Interfaces using manager classes
public class HandlerA: IHandler
{
private readonly DbContext _dbContext;
public HandlerA(DbContext dbContext)
{
_dbContext = dbContext;
}
string method1(Entity entity)
{
//access db or business stuff
_dbContext.Entities.Add(entity);
_dbContext.SaveChanges();
}
int method2(){}
}
public class HandlerB: IHandler
{
string method1(Entity entity)
{
//access db or business stuffs
_dbContext.Entities.Remove(entity);
_dbContext.SaveChanges();
}
int method2(){}
int method3(){}
}
//Services
[Service]
public class DoService1Stuff()
{
private readonly HandlerA _handlerA;
public DoService1Stuff(HandlerA handlerA)
{
_handlerA= handlerA;
}
//Implement your task
public Do(Entity entity)
{
_handlerA.method1(entity);
}
}
[Service]
public class DoService2Stuff()
{
private readonly HandlerB _handlerB;
public DoService2Stuff(HandlerB handlerB)
{
_handlerB= handlerB;
}
//Implement your task
public Do(Entity entity)
{
_handlerA.method1(entity);
}
}
Register services through dependency injection in startup.cs
services.AddTransient<IHandler, HandlerA>();
services.AddTransient<IHandler, HandlerB>();
Access services in controllers
[HttpPost]
public async Task<IActionResult> DoStuff1([FromServicec] DoService1Stuff doService1Stuff)
{
var entity= new Entity
{
str="hello",
numb=2020;
};
return Ok(doService1Stuff.Do(entity))
}
//Implement second service as needed.

Configuring DBContext in the constructor of my base repository class

I have a situation where I need to instantiate my DBContext after my solution has started up. I asked this question which indicated that I could do this with a constructor argument.
It was suggested that I implement as an example this:
var connection = #"Server=(localdb)\mssqllocaldb;Database=JobsLedgerDB;Trusted_Connection=True;ConnectRetryCount=0";
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer(connection);
using (var context = new BloggingContext(optionsBuilder.Options))
{
// do stuff
}
However I have implemented the repository pattern (for better or worst) and given my changed circumstances - not having a connection string until after the solution has run startup - I need to implement this into the base repository class and I am at a bit of a loss..
Currently I have this:
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntityBase, new()
{
public JobsLedgerAPIContext _context;
#region Properties
public EntityBaseRepository(JobsLedgerAPIContext context)
{
_context = context;
}
#endregion
public virtual IQueryable<T> GetAll()
{
return _context.Set<T>().AsQueryable();
}
public virtual int Count()
{
return _context.Set<T>().Count();
}
......
How do I implement this change both instantiating the DBContext in the constructor (there by bypassing the need to add the context as a service in startup) and then with the wrapping each of the virtual methods with "using" etc
EDIT.. Camilo indicated I had not identified when I have the database name.
The basic situation is that the system starts up (This is an Aurelia SPA project which is irrelevant to this issue) sends the package to the browser which shows a login screen. User logs in.. User is verified via a JWT controller.. Once verified in the controller (using a catalog database that has one table with 3 fields - username, password, database name) I use the database name to create a connection string and then instantiate my DBContext at that point.. so via a constructor.
The answers below need to be modified as the one with the factory answer (promising) has errors as discovered by this question.. Nkosi responded with an great answer to the error.
EDIT 2..
This is a response to the edited question below:
Here is my original Client Repository with :base(context) on the constructor.
using JobsLedger.DATA.Abstract;
using JobsLedger.MODEL.Entities;
namespace JobsLedger.DATA.Repositories
{
public class ClientRepository : EntityBaseRepository<Client>, IClientRepository
{
private new JobsLedgerAPIContext _context;
public ClientRepository(JobsLedgerAPIContext context) : base(context)
{
_context = context;
}
public void RelatedSuburbEntities(Suburb _suburb)
{
_context.Entry(_suburb).Reference<State>(a => a.State).Load();
}
}
}
It has a reference to the base class "context". I am not sure how to modify this given that I believe I still need that ":base(context)" at the end. As well, I have a method in this that accesses _context as well which is part of the constructor...
Further I assume that I can no longer inject the service into the controller but instead new it up once I have secured the connection string and then pass that connection string to service.
Also, Given I have now added a singleton on the startup do I need to remove the original entry? :
services.AddDbContext<JobsLedgerAPIContext>(options => options.
UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly("JobsLedger.API")));
effectively replacing it with my singleton reference as per below:
services.AddSingleton(typeof(IContextFactory<>), typeof(ContextFactory<>));
Edited
The answer has been edited to rectify the mistake spotted and
fixed by Nkosi. Thanks, #Nkosi.
Implement a factory pattern. You can create a factory, call it ContextFactory as below:
First, define the interface. Further modified, removed the connectionString parameter
public interface IContextFactory<T> where T : DbContext
{
T CreateDbContext();
}
Create a factory class that implements this interface (edited as per Nkosi answer). Further modified to inject IHttpContextAccessor
public class ContextFactory<T> : IContextFactory<T> where T : DbContext
{
private readonly HttpContext _httpContext;
public ContextFactory(IHttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
public T CreateDbContext()
{
// retreive the connectionString from the _httpContext.Items
// this is saved in the controller action method
var connectionString = (string)_httpContext.Items["connection-string"];
var optionsBuilder = new DbContextOptionsBuilder<T>();
optionsBuilder.UseSqlServer(connectionString);
return (T)Activator.CreateInstance(typeof(T), optionsBuilder.Options);
}
}
Then modify your base repository and make the JobsLedgerAPIContext protected. This context is going to be set by the derived class. Further modified to remove the constructor. It will use the parameterless constructor.
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntityBase, new()
{
protected JobsLedgerApiContext Context { get; set; }
public virtual IQueryable<T> GetAll()
{
return Context.Set<T>().AsQueryable();
}
public virtual int Count()
{
return Context.Set<T>().Count();
}
}
Change your derived class to use IContextFactory. Further modified to use the _contextFactory.CreateDbContext() parameter less method
The IClientRepository should have SetContext method defined.
public class ClientRepository : EntityBaseRepository<Client>, IClientRepository
{
private readonly IContextFactory<JobsLedgerApiContext> _contextFactory;
public ClientRepository(IContextFactory<JobsLedgerApiContext> factory)
{
_contextFactory = factory;
}
// this method will set the protected Context property using the context
// created by the factory
public void SetContext()
{
Context = _contextFactory.CreateDbContext();
}
public void RelatedSuburbEntities(Suburb suburb)
{
Context.Entry(suburb).Reference<State>(a => a.State).Load();
}
}
In the controller, that receives IClientRepository instance, you can set the connection in the HttpContext.Items, which will be valid for the request. This value will then be retrieved by the ContextFactory using IHttpContextAccessor. Then you simply call the _repository.SetContext(); method on the repository.
public class HomeController : Controller
{
private readonly IClientRepository _repository;
public HomeController(IClientRepository repository)
{
_repository = repository;
}
public IActionResult Index()
{
// save the connectionString in the HttpContext.Items
HttpContext.Items["connection-string"] = "test-connection";
// set the context
_repository.SetContext();
return View();
}
}
Make sure you register the IContextFactory in ConfigureServices as open generics and Singleton as below, also register the HttpContextAccessor and IClientRepository
services.AddHttpContextAccessor();
services.AddSingleton(typeof(IContextFactory<>), typeof(ContextFactory<>));
services.AddTransient<IClientRepository, ClientRepository>();
You may define your JobsLedgerAPIContext like this:
public class JobsLedgerAPIContext : DbContext
{
// public DbSet<Job> Jobs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=dotnetcore;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// may need to reflect entity classes and register them here.
base.OnModelCreating(modelBuilder);
}
}

Single Responsibility Principle Concerns (Am I thinking about refactoring properly)

My current class PropertyManager looks like this:
public class PropertyManager : IDisposable
{
private readonly IPropertyRepo _propertyRepo;
private readonly IUserTypeRepo _userTypeRepo;
public PropertyManager(IPropertyRepo propertyRepo, IUserTypeRepo userTypeRepo = null)
{
if (propertyRepo == null)
throw new ArgumentNullException("propertyRepo");
_propertyRepo = propertyRepo;
if (userTypeRepo != null)
_userTypeRepo = userTypeRepo;
}
}
My Property Manager will use the _userTypeRepo in some method to accomplish some task. I think I want to implment a rule that says "Each Manager(Service,Factory,etc) should be responsible for its own repository."
The idea:
The PropertyManager, because it needs to do something with the UserTypeRepo, I should be using the UserManager for such activities.
As such, this means that I will not provide a repo when creating an instance of the UserManager (i.e., var usrMgr = new UserManager(); // no repo). Instead, the UserManager will use the default constructor which will create a new instance of the IUserTypeRepo and provide a new instance of a UserManager and then it can do its work.
I think this accomplishes some design principle such as Separation of Concerns and the Single Responsibility, but then I may be getting away from my Dependency Injection design pattern as the new Managers would now have multiple constructors and look like this:
public class PropertyManager : IDisposable
{
private readonly IPropertyRepo _propertyRepo;
public PropertyManager(){
// use the default repo
_propertyRepo = new PropertyRepo();
}
// Used from Controller or Unit Testing
public PropertyManager(IPropertyRepo propertyRepo)
{
if (propertyRepo == null)
throw new ArgumentNullException("propertyRepo");
}
}
public class UserManager : IDisposable
{
private readonly IUserRepo _userRepo;
public UserManager(){
// use the default repo
_userRepo = new UserRepo();
}
// Used from Controller or Unit Testing
public UserManager(IUserRepo userRepo)
{
if (userRepo == null)
throw new ArgumentNullException("userRepo");
}
}
Would this be frowned upon? Or am I on the right track? In either case, why and thanks?
Update. After reading Yawar's post I decided to update my post and I think I have a relevant concern.
Let's think of a real world example of the above. I have a PropertyManager in real life named "Robert" one of the jobs he performs each morning at work is to Open() the Property (i.e., he unlocks the Property he is the Manager of). I also have a UserManger who manages people who visit the Property and her name is "Sarah" she has a function that she does called EnterProperty() (which is what she does in the morning when she physically walks into the building).
Rule: UserManager has a dependency on PropertyManager when using the EnterProperty()
This looks like this according to all accepted standards:
Property Manager
class PropertyManager : IPropertyManager
{
private readonly IPropertyRepo _propertyRepo;
public PropertyManager(IPropertyRepo propertyRepo)
{
if (propertyRepo == null)
throw new ArgumentNullException("propertyRepo");
this._propertyRepo = propertyRepo;
}
// this is when Robert opens the property in the morning
public void Open()
{
_propertyRepo.Open();
}
// this is when Robert closes the property in the evening
public void Close()
{
_propertyRepo.Close();
}
// this answers the question
public bool IsOpen()
{
return _propertyRepo.IsOpen();
}
}
User Manager
class UserManager : IUserManager
{
private readonly IPropertyRepo _propertyRepo;
private readonly IUserRepo _userRepo;
public UserManager(IUserRepo userRepo, IPropertyRepo propertyRepo = null)
{
if (userRepo == null)
throw new ArgumentNullException("userRepo");
this._userRepo = userRepo;
if (propertyRepo != null)
this._propertyRepo = propertyRepo;
}
// this allows Sarah to physically enter the building
public void EnterProperty()
{
if(_propertyRepo.IsOpen())
{
Console.WriteLine("I'm in the building.");
}else{
_propertyRepo.Open(); // here is my issue (explain below)
Console.WriteLine("Even though I had to execute the Open() operation, I'm in the building. Hmm...");
}
}
}
Web API Controller
{
public void OpenForBusiness(){
private const IPropertyRepo propertyRepo = new PropertyRepo();
private IPropertyManager propertyManager = new PropertyManager(propertyRepo);
private IUserManager userManager = new UserManager(new UserRepo(), propertyRepo);
// Robert, the `PropertyManager`, opens the `Property` in the morning
propertyManager.Open();
// Sarah, the `UserManager`, goes into `Property` after it is opened
userManager.EnterProperty();
}
}
Now, everything is cool and I can walk away and I now have a Repository Pattern which use Dependency Injection which supports TDD and not tightly coupled classes among other benefits.
However, is the truly realistic? (explain why I ask in second)
I think a more real-world (realistic) approach is one that does:
Web API Controller
public void Method1()
{
private IPropertyManager propMgr = new PropertyManager(new PropertyRepo());
private IUserManager userMgr = new UserManager(new UserRepo()); // no dependencies on any repository but my own
// 1. Robert, the `PropertyManager`, opens the `Property`
propMgr.Open();
// 2. Check to see if `Property` is open before entering
// choice a. try to open the door of the `Property`
// choice b. call or text Robert, the `PropertyManager`, and ask him if he opened the `Property` yet, so...
if(propMgr.IsOpen()){
// 3. Sarah, the `UserManager`, arrives at work and enters the `Property`
userMgr.EnterProperty();
}else{
// sol, that sucks, I can't enter the `Property` until the authorized person - Robert - the `PropertyManager` opens it
// right???
}
}
the EnterProperty() method on the UserManager now looks like this:
// this allows Sarah to physically enter the building
public void EnterProperty()
{
Console.WriteLine("I'm in the building.");
}
The promised explanation from above:
If we think in real-world terms we must agree that the later is preferred over the former. When thinking of a Repository lets say this is the definition of ones self (i.e., one's Person) (i.e., the UserRepo having all the data related to the User, is to the UserManager as the DNA, Heartbeat, Brain Wave Pattern, etc. is to a Human (the HumanRepo). As such, allowing the UserManager to know about the PropertyRepo and having access to its Open() method violates all Real-World security principles and Business Rules. In reality this says that through My Contructor() I can get an Interface Representation of a PropertyRepo that I can use any way I see fit. This is synonymous to the following logic of the HumanRepo:
I, Sarah - a UserManager - through a new instance of myself with the satisfaction of the PropertyRepo through my Constructor() create a Hologram Interface of Robert, the PropertyManager that I can use any way I see fit. Granted right now I only want to use the IsOpen() method of the PropertyRepo I actually use the Open() method to do it myself if Robert has not yet performed his duty. This is a security concern to me. In the real-world this says I don't have to wait for Robert to open the Property and use the Holocopy of him and implement his Open() method to get access.
That doesn't seem right.
I think with the last implementation I get SoC, SRP, DI, Repository Pattern, TDD, and Logical Security and as close to a real-world implementation as possible.
What do you all think?
I think I agree with your SoC and breaking the PropertyManager class into PropertyManager and UserManager classes. You are almost there.
I would just refactor as shown below:
public class PropertyManager : IDisposable, IPropertyManager
{
private readonly IPropertyRepo _propertyRepo;
// Used from Controller or Unit Testing
public PropertyManager(IPropertyRepo propertyRepo)
{
if (propertyRepo == null)
throw new ArgumentNullException("propertyRepo");
this._propertyRepo = propertyRepo;
}
}
public class UserManager : IDisposable, IUserManager
{
private readonly IUserRepo _userRepo;
// Used from Controller or Unit Testing
public UserManager(IUserRepo userRepo)
{
if (userRepo == null)
throw new ArgumentNullException("userRepo");
this._userRepo = userRepo;
}
}
Note: Just extract IPropertyManager & IUserManager so that the calling classes will depend upon the interfaces and provide the implementation.
Creating parameterless constructor is useless if you want to (you should) force the client to provide the concrete implementation of IPropertyRepo and IUserRepo interfaces.
public PropertyManager(){
// use the default repo
_propertyRepo = new PropertyRepo();
}
I dont think you would need
if (propertyRepo == null)
throw new ArgumentNullException("propertyRepo");
or
if (userRepo == null)
throw new ArgumentNullException("userRepo");
as IPropertyRepo and IUserRepo will be resolved via a IoC at the startup of your application (say its MVC then before calling the controller IoC will resolve them) so no need to check for null. I have never checked the dependencies for null in my code.
From what you have posted here thats pretty much it.
Unit of Work pattern is used for repository layer not in the manager layer. I would delete that from the title.
Hope this helps!
I think this accomplishes some OOP goal such as Separating Concerns
and the Single Responsibility Principle.
The result is opposite. Now, PropertyManager tightly couples to PropertyRepo; previously, they were loosely coupled.
First approach is better than the latter one. However, PropertyManager and UserManager should not create other objects on which they rely to do their work. The responsibility for creating and managing object should be offloaded to IoC container.
Interfaces describe what can be done, whereas classes describe how it is done. Only classes involve the implementation details—interfaces are completely unaware of how something is accomplished. Because only classes have constructors, it follows that constructors are an implementation detail. An
interesting corollary to this is that, aside from a few exceptions, you can consider an appearance of the new keyword to be a code smell. - Gary McLean Hall
Answer for Updated Question:
In your updated question, you combine Service/Manager and somewhat Domain into a single class - PropertyManager, UserManager. It becomes personal preference.
I personally like to keep them separate. In addition, I like to use Role based and Claim based authorization. Let me use my GitHub sample project as a reference. Please feel free to clone it.
User Domain
User class is also used by Entity Framework Code First Fluent API.
public partial class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
}
User Service
public class UserService : IUserService
{
private readonly IRepository<User> _repository;
public UserService(IRepository<User> repository)
{
_repository = repository;
}
public async Task<IPagedList<User>> GetUsersAsync(UserPagedDataRequest request)
{
...
}
}
Action Method
Notice that UI related Business Logic stays at UI layer.
public async Task<ActionResult> Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
bool result = _activeDirectoryService.ValidateCredentials(
model.Domain, model.UserName, model.Password);
if (result)
{
...
}
}
...
}
you can take quite a bit of a different approach.....( ignoring your repositories, but allowing for it to be injected )
In this system, the property is only readable, with an event system to handle the mutations, the event system also has rules system which controls what mutations are allowed. This means even if you have a property object you can't mutate it without going through its rules.
This code is more conceptual. The next logical step is to use a full actor model and something like (akka.net) and you may find your repository pattern just disappearing :)
public class Property
{
public string Name { get; private set; }
private IPropertyRules _rules;
private List<User> _occupants = new List<User>();
private IEventLog _eventLog;
public Property(IPropertyRules rules, IEventLog eventLog)
{
_rules = rules;
_eventLog = eventLog;
}
public ActionResult Do(IAction action, User user)
{
_eventLog.Add(action, user);
if (_rules.UserAllowedTo(action, user, this))
{
switch (action)
{
case Open o:
Open();
return new ActionResult(true, $"{user} opened {Name}");
case Enter e:
Enter(user);
return new ActionResult(true, $"{user} entered {Name}");
}
return new ActionResult(false, $"{Name} does not know how to {action} for {user}");
}
return new ActionResult(false, $"{user} is not allowed to {action} {Name}");
}
private void Enter(User user)
{
_occupants.Add(user);
}
private void Open()
{
IsOpen = true;
}
public bool IsOpen { get; set; }
}
public interface IEventLog
{
void Add(IAction action, User user);
}
public class Enter : IAction
{
}
public interface IPropertyRules
{
bool UserAllowedTo(IAction action, User user, Property property);
}
public class Open : IAction
{
}
public class ActionResult
{
public ActionResult(bool successful, string why)
{
Successful = successful;
WhatHappened = why;
}
public bool Successful { get; private set; }
public string WhatHappened { get; private set; }
}
public interface IAction
{
}
public class User
{
}

Abstract Factory and Inversion of Control resolve at runtime

I have the following class and interface structure and I'm having a hard time trying to get the code to do what I need.
public interface IUserManager
{
int Add(User user);
}
public class UserManagerA : IUserManager{}
public class UserManagerB : IUserManager{}
In this example I'm using Ninject as the IoC container but I'm open to changing it if some other container resolves the issue:
This is inside my NinjectWebCommon.cs:
void RegisterServices(IKernel kernel)
{
string userRole = CurrentUser.Role;//this gets the user logged in
//This is the part I do not how to do
//I wish I could just type this in:
kernel.Bind<IUserManager>().To<UserManagerA>()
.When(userRole == "RoleA"); // this doesn't work obviously
kernel.Bind<IUserManager>().To<UserManagerB>()
.When(userRole == "RoleB"); // same doesn't work
}
All of that so that in my (MVC) controller I can do this:
public class UserController
{
private readonly IUserManager _userManager;
public UserController(IUserManager userManager)
{
_userManager = userManager;
}
public ActionResult Add(User user)
{
//this would call the correct manager
//based on the userRole
_userManager.Add(user);
}
}
I've been reading articles about Abstract Factory but haven't found one that explains how to integrate the factory with the IoC container and pass a parameter obtained at run-time to resolve the implementations.
Create a class responsible for providing the correct UserManager and inject this to your controller:
public class UserManagerProvider : IUserManagerProvider
{
private readonly IContext _context;
public UserManagerProvider(IContext context)
{
_context = context;
}
public IUserManager Create(User currentUser)
{
if (currentUser.Role == "User A")
return _context.Kernel.Get<UserManagerA>();
if (currentUser.Role == "User B")
return _context.Kernel.Get<UserManagerB>();
// Or bind and resolve by name
// _context.Kernel.Get<IUserManager>(currentUser.Role);
}
}
And in controller:
private readonly IUserManager _userManager;
public UserController(IUserManagerProvider userManagerProvider)
{
_userManager = userManagerProvider.Create(CurrentUser);
}
Also, as a side note you should probably have a CurrentUserProvider responsible for getting the current user. Relying on a static method will make things difficult to unit test and you're essentially hiding a dependency in all classes that reference it:
private readonly IUserManager _userManager;
private readonly User _currentUser;
public UserController(IUserManagerProvider userManagerProvider, ICurrentUserProvider currentUserProvider)
{
_currentUser = currentUserProvider.GetUser();
_userManager = userManagerProvider.Create(_currentUser);
}
Provided the number of IUserManager implementations is not very many (not likely to reach 100 implementations), you can use a Strategy Pattern to resolve all of your UserManager instances during composition and then pick the best instance for use at runtime.
First, we need a way to map IUserManager implementations to roles.
public interface IUserManager
{
int Add(User user);
bool AppliesTo(string userRole);
}
public class UserManagerA : IUserManager
{
// Add method omitted
public bool AppliesTo(string userRole)
{
// Note that it is entirely possible to
// make this work with multiple roles and/or
// multiple conditions.
return (userRole == "RoleA");
}
}
public class UserManagerB : IUserManager
{
// Add method omitted
public bool AppliesTo(string userRole)
{
return (userRole == "RoleB");
}
}
Then we need a strategy class that simply picks the correct instance based on the userRole. The IUserManager instances are supplied by the DI container when the application is composed.
public interface IUserManagerStrategy
{
IUserManager GetManager(string userRole);
}
public class UserManagerStrategy
: IUserManagerStrategy
{
private readonly IUserManager[] userManagers;
public UserManagerStrategy(IUserManager[] userManagers)
{
if (userManagers == null)
throw new ArgumentNullException("userManagers");
this.userManagers = userManagers;
}
public IUserManager GetManager(string userRole)
{
var manager = this.userManagers.FirstOrDefault(x => x.AppliesTo(userRole));
if (manager == null && !string.IsNullOrEmpty(userRole))
{
// Note that you could optionally specify a default value
// here instead of throwing an exception.
throw new Exception(string.Format("User Manager for {0} not found", userRole));
}
return manager;
}
}
Usage
public class SomeService : ISomeService
{
private readonly IUserManagerStrategy userManagerStrategy;
public SomeService(IUserManagerStrategy userManagerStrategy)
{
if (userManagerStrategy == null)
throw new ArgumentNullException("userManagerStrategy");
this.userManagerStrategy = userManagerStrategy;
}
public void DoSomething()
{
string userRole = CurrentUser.Role;//this gets the user logged in
// Get the correct UserManger according to the role
IUserManager userManager = this.userManagerStrategy.GetManger(userRole);
// Do something with userManger
}
}
void RegisterServices(IKernel kernel)
{
kernel.Bind<IUserManager>().To<UserManagerA>();
kernel.Bind<IUserManager>().To<UserManagerB>();
// Ninject will automatically supply both IUserManager instances here
kernel.Bind<IUserManagerStrategy>().To<UserManagerStrategy>();
kernel.Bind<ISomeService>().To<SomeService>();
}
This method doesn't require you to inject the container into the application. There is no service location being used.
Note also that there is no switch case statement that would have to be modified every time you add a new UserManager to the application. The logic of when to use a UserManager is part of the UserManager implementation and the order in which the logic is executed is determined by the DI configuration.
In addition, this will work regardless of which DI container you are using.
You could combine this with the CurrentUserProvider from RagtimeWilly's answer for a clean way to get the user role into the service where this is used.
Reference: Best way to use StructureMap to implement Strategy pattern

How to model this in DDD and repository pattern

I want to model service like this
public class FooService
{
public GetById(ISecurityContext context, id)
{
//checking context has right to view
//calling Foo repository to getById
}
public Add(ISecurityContext context,Foo fooEntity)
{
//checking context has right to add
//calling Foo repository to add
}
}
In the above methods i want to pass different type of SecurityContext
So what i have did is
Public Interface ISecurityContext
{
}
UsernamePasswordContext : ISecurityContext
{
public string Username { get; set; }
public string Password { get;set; }
}
SessionContext : ISecurityContext
{
public string SessionId {get ; set;}
}
So In my Account Service i have a method
public class AccountService
{
public Account GetAccountFromSecurityContext(ISecurityContext context)
{
if(context is UsernamePasswordContext)
return GetAccountByUsernamePassword(context.Username,context.Password);
else if (context is SessionContext)
return GetAccountBySessionId(context.SessionId);
// more else if for different type of context
}
}
In the above code i didnt liked so many if else
So i tried introducing polymorphism
So in my ISecurityContext interface i added a GetAccount method which all sub class will implement
Public Interface ISecurityContext
{
Account GetAccount();
}
UsernamePasswordContext : ISecurityContext
{
public string Username { get; set; }
public string Password { get;set; }
public Account GetAccount()
{
//call account service
GetAccountByUsernamePassword(this.Username,this.Password);
}
}
and my account service will become like this
public class AccountService
{
public Account GetAccountFromSecurityContext(ISecurityContext context)
{
context.GetAccount();
}
}
But the problem here is that i am calling a service/repository from my UsernamePasswordContext POCO which voilates DDD
So what are other ways i can model this scenario.
I think you're not far off from the solution. In this case, I would inject a factory into your AccountService that would take on the responsibility of the if..then..else. Then, the factory could use one of many possible solutions.
One change I would make right off is I would make your AccountService implement an interface which should make it easier to inject later. Assuming you're using some IOC container, you shouldn't have to worry too much about dependencies because you're letting the container handle all that.
Here are the pieces you already had, with some minor ajustments:
public class Account
{
//some account information and behavior
}
public interface ISecurityContext
{
}
public class UsernamePasswordContext : ISecurityContext
{
public string Username { get; set; }
public string Password { get; set; }
}
public class SessionContext : ISecurityContext
{
public string SessionId { get; set; }
}
Here's your account service along with it's implementation:
public interface IAccountService
{
Account GetAccountFromSecurityContext(ISecurityContext securityContext);
}
public class AccountService : IAccountService
{
readonly IAccountFactory _accountFactory;
public AccountService(IAccountFactory accountFactory)
{
_accountFactory = accountFactory;
}
public Account GetAccountFromSecurityContext(ISecurityContext securityContext)
{
Account account = _accountFactory.Create(securityContext);
return account;
}
}
So, you can see here that I've injected an IAccountFactory that will handle the actual creation (retrieval, whatever) of the Account object. All we care about at this point is that the account gets created/retrieved... we don't care about how.
There are a few ways you can implement a factory like this. One way is to use a type of strategy pattern where you have a list of widgets that know how to resolve an account. Then you just pick the widget (strategy) that matches and execute it. Something similar to this would be a factory that uses an IOC or service locator to resolve a type that has been registered previously in application configuration.
In the way of an example, here's one possible implementation of IAccountFactory using CommonServiceLocator:
public interface IAccountFactory
{
Account Create(ISecurityContext securityContext);
}
public class ServiceLocatorAccountFactory : IAccountFactory
{
readonly IServiceLocator _serviceLocator;
public ServiceLocatorAccountFactory(IServiceLocator serviceLocator)
{
_serviceLocator = serviceLocator;
}
public Account Create(ISecurityContext securityContext)
{
var resolverType = typeof (IAccountResolver<>).MakeGenericType(securityContext.GetType());
dynamic resolver = _serviceLocator.GetInstance(resolverType);
return resolver.Resolve(securityContext);
}
}
My factory here goes out to the service locator context and grabs whatever resolver matches our security context. Here are a couple examples of possible resolvers:
public interface IAccountResolver<in TSecurityContext> where TSecurityContext : ISecurityContext
{
Account Resolve(TSecurityContext securityContext);
}
public class UsernamePasswordAccountResolver : IAccountResolver<UsernamePasswordContext>
{
readonly IRepository _repository;
public UsernamePasswordAccountResolver(IRepository repository)
{
_repository = repository;
}
public Account Resolve(UsernamePasswordContext securityContext)
{
var account = _repository.GetByUsernameAndPassword(securityContext.Username,
securityContext.Password);
return account;
}
}
public class SessionAccountResolver : IAccountResolver<SessionContext>
{
public Account Resolve(SessionContext securityContext)
{
//get the account using the session information
return someAccount;
}
}
The only thing left is to register the resolvers in your IOC container so that they can be found when the service locator tries to resolve them in the factory.

Categories

Resources