injecting different implementations and configurations of same interface into different clients - c#-4.0

Suppose I have an interface IStorage and multiple implementations of it, e.g.:
class FileStorage : IStorage
{
public FileStorage(string filePath)
{
}
}
class HttpStorage : Storage
{
public HttpStorage(IHttpClient httpClient)
{
}
}
Now I have multiple classes that I want to register in my application and each of them needs a different IStorage instance.
All instances of ClassA (implementing and registered via IClassA) need a singleton FileStorage with "C:\Temp\foo.txt" as filePath.
All instances of ClassB (implementing and registered via IClassB) need a singleton FileStorage with "C:\Temp\bar.txt" as filePath.
All instances of ClassC (implementing and registered via IClassC) need a singleton HttpStorage with the registered singleton of IHttpClient.
How can I achieve the above without falling back to creating most of the dependency graph manually?

The primary question to ask every time you think you need this is: Do I violate the Liskov Substitution Principle. You are breaking the LSP in case the implementations aren't interchangeable for one another. If ClassA breaks when you inject an HttpStorage into it, you are breaking the LSP. In that case, you should give each implementation each own abstraction, such as IFileStorage and IHttpStorage.
My first impression is that you are not violating LSP. Simple Injector v3 contains a RegisterConditional method that allows you to do conditional registrations. With Simple Injector v3.1 you can make the registration as follows:
Lifestyle transient = Lifestyle.Transient;
container.RegisterConditional(typeof(IStorage),
transient.CreateRegistration(() => new FileStorage(#"C:\Temp\foo.txt"), container),
c => c.Consumer.ImplementationType == typeof(ClassA));
container.RegisterConditional(typeof(IStorage),
transient.CreateRegistration(() => new FileStorage(#"C:\Temp\bar.txt"), container),
c => c.Consumer.ImplementationType == typeof(ClassB));
container.RegisterConditional(typeof(IStorage),
transient.CreateRegistration(() => new HttpStorage(new HttpClient()), container),
c => c.Consumer.ImplementationType == typeof(ClassC));
The RegisterConditional overload that accepts an Registration instance is new in v3.1.

Related

asp.net core identity simpleinjector register applicationusermanager

i'm try isolation ASP.NET Core Identity version 1.1.2, architecture DDD and creating a CrossCutting layer to create a classlib for aspnet core identity , and i using SimpleInjector 4.0.8 for my IoC, so i create a class ApplicationUserManager and ApplicationSignInManager, but i'm cannot register this class in the simlpleinjector container
ApplicationUserManager
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser> passwordHasher,
IEnumerable<IUserValidator<ApplicationUser>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators,
ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<ApplicationUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
// My configurations stuffs were...
}
}
and the class ApplicationSignInManager
public class ApplicationSignInManager : SignInManager<ApplicationUser>
{
public ApplicationSignInManager(UserManager<ApplicationUser> userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory,
IOptions<IdentityOptions> optionsAccessor,
ILogger<SignInManager<ApplicationUser>> logger)
: base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger)
{ }
// TODO: bug com tipo de retorno IdentityResult para ClaimsPrincipal
//public override Task<ClaimsPrincipal> CreateUserPrincipalAsync(ApplicationUser user)
//{
// return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
//}
}
and i try register this class in BootStrapper.cs like this
public static void RegisterServices(Container container)
{
// Infra.Data App Context
// IdentityAppDbContext
container.RegisterSingleton(() =>
{
var options = new DbContextOptions<IdentityAppDbContext>();
return new IdentityAppDbContext(options);
});
// NetCore Identity
container.RegisterSingleton<ApplicationUserManager>();
container.RegisterSingleton<ApplicationSignInManager>();
container.RegisterSingleton<IUserStore<ApplicationUser>>(() =>
{
var options = new DbContextOptions<IdentityAppDbContext>();
return new UserStore<ApplicationUser>(new IdentityAppDbContext(options));
});
container.Register(() => (IOptions<IdentityOptions>)new IdentityOptions());
container.RegisterSingleton<IPasswordHasher<ApplicationUser>>(() => new PasswordHasher<ApplicationUser>());
}
but when i run the aaplication return erros says i need registre IOptions, IPasswordHasher and other params in contructors class, the queston is, how can'i register this class?
I had a similar requirement: my application needs to interact with identity (it's basically about human resources, that might have a user account on their own or not), and after struggling a long time with with various issues having identity in the framework container, I was trying to configure simple injector to provide all identity services. Call it crazy, but this is how it works (SimpleInjector 4.0 and ASP.Net Identity Core 2.0.1):
This is the "ConfigureServices" part:
// identity options are provided from outside, allowing configuration of the framework
container.RegisterSingleton<IOptions<IdentityOptions>>(new OptionsWrapper<IdentityOptions>(identityOptions));
// we rely on BCrypt instead of the default PBKDF2 hashing algorithm
container.Register<IPasswordHasher<MepUser>>(()=>new BCryptPasswordHasher(bcryptOptions));
// forwarding the framework logger to our own logging framework
container.Register<Microsoft.Extensions.Logging.ILoggerFactory, FrameworkToBackendFxLoggerFactory>();
container.Register(typeof(Microsoft.Extensions.Logging.ILogger<>), typeof(Microsoft.Extensions.Logging.Logger<>));
// identity store = a specific Entity Framework Core DbContext, getting mapped into a specific db scheme
container.RegisterSingleton(identityDbContextOptions);
container.Register<MepIdentityDbContext>();
// UserStore<T> und RoleStore<T> both require a DbContext (no IdentityDbContext, neither a generic TDbContext)
// via constructor, but the container only knows about MepIdentityDbContext so we have to wire it manually
container.Register<IUserStore<MepUser>>(() => new UserStore<MepUser>(
container.GetInstance<MepIdentityDbContext>(),
container.GetInstance<IdentityErrorDescriber>()));
container.Register<IRoleStore<IdentityRole>>(() => new RoleStore<IdentityRole>(
container.GetInstance<MepIdentityDbContext>(),
container.GetInstance<IdentityErrorDescriber>()));
// framework internal services
container.Register<IdentityErrorDescriber>();
container.Register<ILookupNormalizer, UpperInvariantLookupNormalizer>();
container.Register<IPasswordValidator<MepUser>, PasswordValidator<MepUser>>();
container.Register<IUserValidator<MepUser>, UserValidator<MepUser>>();
container.Register<IUserClaimsPrincipalFactory<MepUser>, UserClaimsPrincipalFactory<MepUser>>();
container.Register<IRoleValidator<IdentityRole>, RoleValidator<IdentityRole>>();
// ASP.Net Core Identity violates a design decision of SimpleInjector: The API clearly differentiates the registration of collections
// https://simpleinjector.readthedocs.io/en/latest/decisions.html#the-api-clearly-differentiates-the-registration-of-collections
// By registering IEnumerables of those violating services with a simple wrapping single item array, Identity is happy
container.Register<IEnumerable<IUserValidator<MepUser>>>(() => new[] { container.GetInstance<IUserValidator<MepUser>>() });
container.Register<IEnumerable<IRoleValidator<IdentityRole>>>(() => new[] { container.GetInstance<IRoleValidator<IdentityRole>>() });
container.Register<IEnumerable<IPasswordValidator<MepUser>>>(() => new[] { container.GetInstance<IPasswordValidator<MepUser>>() });
// Role and UserManager reflect the API surface of the whole ASP.Net Core Identity framework
container.Register<RoleManager<IdentityRole>>();
// UserManagerShim is omitting (=nulling) the IServiceProvider parameter of UserManager<T>
container.Register<UserManager<MepUser>, UserManagerShim>();
and this needs to be done during "Configure", otherwise password reset and other two factor token stuff won't work (runtime error, although the container was verified)
// if you eagerly instantiate a provider instance that is considered being a singleton and
// put it into the respective field in the TokenProviderDescriptor and list it in the option's
// provider map, ASP.Net Core Identity will use this one instead of asking the IServiceProvider
// instance injected into UserManager<T> (that we do not do, because it is bad design. Instead,
// we just stuff null in there)
identityOptions.Tokens.ProviderMap[TokenOptions.DefaultProvider] =
new TokenProviderDescriptor(typeof(DataProtectorTokenProvider<MepUser>))
{
ProviderInstance = new DataProtectorTokenProvider<MepUser>(
dataProtectionProvider,
new OptionsWrapper<DataProtectionTokenProviderOptions>(new DataProtectionTokenProviderOptions()))
};
identityOptions.Tokens.ProviderMap[TokenOptions.DefaultEmailProvider] =
new TokenProviderDescriptor(typeof(EmailTokenProvider<MepUser>)) {
ProviderInstance = new EmailTokenProvider<MepUser>()
};
Although there appears AspNetCore in the namespace definition, nothing is dependent on ASP.Net Core hosting, actually. The only dependency to the "outside world" are the options (POCOs) and an implementation of IDataProtectionProvider, that will use the KeyRing in ASP.Net scenarios, but can also be satisfied using the EphemeralDataProtectionProvider in tests.
Caveat: SignInManager<T> is not being injected. This class is a total mess, depending on the whole world, so I rewrote it basically matching my requirements. You also loose lots of the configuration flexibility regarding token providers. But however, in my case it's something you decide once at design time, so I am fine with it.

Is there a way to ignore some entity properties when calling EdmxWriter.WriteEdmx

I am specifically using breezejs and the server code for breeze js converts the dbcontext into a form which is useable on the clientside using EdmxWriter.WriteEdmx. There are many properties which I have added JsonIgnore attributes to so that they don't get passed to the client side. However, the metadata that is generated (and passed to the clientside) from EdmxWriter.WriteEdmx still has those properties. Is there any additional attribute that I can add to those properties that I want ignored so that they are ignored by EdmxWriter.WriteEdmx? Or, would I need to make a separate method so as not to have any other unintended side effects.
You can sub-class your DbContext with a more restrictive variant that you use solely for metadata generation. You can continue to use your base context for persistence purposes.
The DocCode sample illustrates this technique with its NorthwindMetadataContext which hides the UserSessionId property from the metadata.
It's just a few extra lines of code that do the trick.
public class NorthwindMetadataContext : NorthwindContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Hide from clients
modelBuilder.Entity<Customer>().Ignore(t => t.CustomerID_OLD);
// Ignore UserSessionId in metadata (but keep it in base DbContext)
modelBuilder.Entity<Customer>().Ignore(t => t.UserSessionId);
modelBuilder.Entity<Employee>().Ignore(t => t.UserSessionId);
modelBuilder.Entity<Order>().Ignore(t => t.UserSessionId);
// ... more of the same ...
}
}
The Web API controller delegates to the NorthwindRepository where you'll see that the Metadata property gets metadata from the NorthwindMetadataContext while the other repository members reference an EFContextProvider for the full NorthwindContext.
public class NorthwindRepository
{
public NorthwindRepository()
{
_contextProvider = new EFContextProvider<NorthwindContext>();
}
public string Metadata
{
get
{
// Returns metadata from a dedicated DbContext that is different from
// the DbContext used for other operations
// See NorthwindMetadataContext for more about the scenario behind this.
var metaContextProvider = new EFContextProvider<NorthwindMetadataContext>();
return metaContextProvider.Metadata();
}
}
public SaveResult SaveChanges(JObject saveBundle)
{
PrepareSaveGuard();
return _contextProvider.SaveChanges(saveBundle);
}
public IQueryable<Category> Categories {
get { return Context.Categories; }
}
// ... more members ...
}
Pretty clever, eh?
Just remember that the UserSessionId is still on the server-side class model and could be set by a rogue client's saveChanges requests. DocCode guards against that risk in its SaveChanges validation processing.
You can sub-class your DbContext with a more restrictive variant that you use solely for metadata generation. You can continue to use your base context for persistence purposes.
The DocCode sample illustrates this technique with its NorthwindMetadataContext which hides the UserSessionId property from the metadata.
It's just a few extra lines of code that do the trick.
The Web API controller delegates to the NorthwindRepository where you'll see that the Metadata property gets metadata from the NorthwindMetadataContext while the other repository members reference an EFContextProvider for the full NorthwindContext.
Pretty clever, eh?
If you use the [NotMapped] attribute on a property, then it should be ignored by the EDMX process.

Domain Modelling: Neither an Entity nor a Value Object

In DDD, the domain model consists of entities and value objects, but what do we do when we need something in the model which is neither of these?
For example, I have introduced the following ScheduledItems<T> implementation in order to encapsulate scheduling specifics:
public class ScheduledItems<T>
{
private SortedDictionary<DateTime, T> scheduledItems;
public ScheduledItems()
{
scheduledItems = new SortedDictionary<DateTime, T>();
}
public void ScheduleItem(DateTime scheduledDate, T item)
{
scheduledItems.Add(scheduledDate, item);
}
public void RemoveItem(T item)
{
scheduledItems
.Where(x => x.Value.Equals(item))
.Select(x => x.Key)
.ToList()
.ForEach(k => scheduledItems.Remove(k));
}
}
This class will be used by a couple of entities for scheduling purposes.
At this point, this is neither an Entity (it has no identity) nor a Value Object (it is not immutable).
One solution is to turn it into a Value Object by making it immutable ('adding' or 'removing' items would return a new instance of ScheduledItems).
But is this really necessary for something which is not really associated to the domain? This class could be just like any other .NET collection.
That class looks like a repository for ScheduledItems. So ScheduledItem is the Entity and ScheduledItems is the the Repository with Add(), Remove() methods.
I guess it depends on why the items are sorted.
If they need to be sorted because of certain business rules then this should be part of your domain.
If they need to be sorted to be properly shown in the UI, then this most likely is just a bit of view logic that should not be part of the domain.
If none of the above, I would consider this a collection-like helper class that could be in a part of the infrastructure layer that could be used across the other layers.

Extending Service/IService to add common dependencies

I have the need to extend Service/IService to allow me to register additional resources like other DB connections and custom classes that each individual service may need to get a handle to.
Is the proper way to do this to subclass Service? Also, it is not clear to me if I have another (say) IDbConnection how Funq figures out which Property to inject the value into.
If you have multiple services with the same type you need to register them in funq with a name. Unfortunatly I don't think funq can autowire the properties correctly so you need to manually resolve them.
container.Register<DataContext>("Security", x => new SecurityDataContext());
container.Register<DataContext>("Customers", x => new CustomersDataContext());
container.Register<DataContext>("Reporting", x => new ReportingDataContext());
container.Register<IReportRepository>(x => new ReportRepositoryImpl(x.ResolveNamed<DataContext>("Reporting")));
An alternative approach would be to create a unique interface (even if it has no members) for each type and then use that in funq. This would allow autowiring
container.Register<ISecurityDataContext>(x => new SecurityDataContext());
container.Register<ICustomersDataContext>(x => new CustomersDataContext());
container.Register<IReportingDataContext>(x => new ReportingDataContext());
// this could just be autowired
container.Register<IReportRepository>(x => new ReportRepositoryImpl(x.Resolve<IReportingDataContext>()));
If you still really need to extend Service you can just use standard inheritance in c#
public abstract class BaseService : Service
{
// custom things go here
public string Example() {
return "Hello World";
}
}
public class ReportsService : BaseService
{
public string Get(ListReports request) {
return Example();
}
}
You can configure other DB connections easily without extending the Service , but by just wiring them in the configure method in the AppHost.cs file.

Castle Windsor Factory implementation

I am using a Typed Factory supplied by Windsor Castle DI container. I am quite new to DI containers, so am after a bit of guidance with my solution.
Here is my implementation:
*updated registration & DB call
public interface IAgent { }
public class Agent : IAgent { }
public interface IAgentFactory
{
IAgent Create();
IAgent Create(int agentId);
IAgent Create(AgentDTO agentDTO);
}
class AgentFactory : IAgentFactory
{
public IAgent Create()
{
return InitNewEntity(new Agent());
}
public IAgent Create(int agentId, IDBContext dbContext) //Not happy with this, as it's a dependency that isn't factored out :(
{
return dbContext.GetAgent(agentId);
}
public IAgent Create(AgentDTO agentDTO)
{
Agent agent = InitNewEntity(new Agent());
agent.ParseDTO(agentDTO);
return agent;
}
private IAgent InitNewEntity(IAgent agent)
{
agent.Username = ""; /// + other fields to initialise
agent.DOB = DateTime.Now; /// etc.
return agent;
}
...
Container.AddFacility<TypedFactoryFacility>()
.Register( Component.For<IAgentFactory>()
.ImplementedBy<AgentFactory>());
which I'm using the following call to get some entities
IAgentFactory agentFactory = ViewModel.Container.Resolve<IAgentFactory>();
IAgent agent = agentFactory.Create(); //Creates new Agent entity
agent = agentFactory.Create(66, dbContext); //Looks up entity in database, don't like passing in a db context, another dependency
agent = agentFactory.Create(agentDTO); //Creates Agent entity from DTO object
I have several concerns about this.
There are 3 possible scenarios regarding creating a new Agent,
1: Create a new agent from scratch
2: Create an agent using an existing DTO (Data Transfer Object)
3: Create an agent with a call to the database.
I decided to put all this functionality into a factory, as it seems to fit the bill, however, I am not entirely sure if this is the correct or best way to accomplish this.
The question is, is it possible to leverage all 3 Create scenarios into the DI container Register statement and omit the concrete factory implementation altogether, so that I no longer have an AgentFactory class, but only the IAgentFactory interface.
Another question I have regarding this implementation, is it possible to do a call, such that if I request an Agent entity, Windsor will first make a call to the factory and return the entity created from the factory, rather than create the entity without referencing the factory.
ie.
IAgent agent = Container.Resolve<IAgent>(66);
With a Callstack:
Container -> AgentFactory -> Create(66) -> returns agent from factory.
Which Windsor will intercept and automatically use the factory to create the instance. However, I tried this and it didn't use the factory to create it, it just went ahead and created the Agent without referencing the factory.
I did have this, but it doesn't call the factory methods that I have defined.
Container.AddFacility<TypedFactoryFacility>()
.Register(Component.For<IAgentFactory>().AsFactory(),
Component.For<IAgent>().ImplementedBy<Agent>());
Advice is appreciated, thank you.
Typed Factory is designed to create "behaviour" instance, not "data" instance.
You do not register into the container a "model" component, but compenent to work w/ the model.
In other words you register into the container everything but the model.
You AgentFactory has to be registerted into the container, but that's not a "Typed Factory".
You may use TF for "late dependency" purpose.
I prefer be more decoupled w/ my design and also more "single responsability" oriented.
AgentFactory won't hide a repository (as per your Create from db) within the factory: I will pass a datareader as Create parameter instead.

Resources