Having my own implementation of the IdentityDbContext, I would like it to be able to connect to a custom database according to the user's choice. So I have created 2 constructors for both default database and user selected database.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("Users", throwIfV1Schema: false)
{
//Aici am adaugat
Configuration.ProxyCreationEnabled = true;
Configuration.LazyLoadingEnabled = true;
}
public ApplicationDbContext(String connectionName)
: base(connectionName, throwIfV1Schema: false)
{
//Aici am adaugat
Configuration.ProxyCreationEnabled = true;
Configuration.LazyLoadingEnabled = true;
}
}
My problem is now the way I would bring the custom connectionName into the class.
The constructor is called in this method:
public static string conn;
public static ApplicationDbContext Create()
{
if(conn == null)
return new ApplicationDbContext();
else
return new ApplicationDbContext(conn);
}
Using session variables is impossible because in this context HttpContext.Current is null. Adding a string argument to the Create method is also impossible, because right in the Startup class, before any user selection, Owin decides on using a default database:
app.CreatePerOwinContext(() => ApplicationDbContext.Create());
Even passing an argument there would not help because it wouldn't have been chosen by the user.
What can I do about it?
Thank you very much!
I would use cookies.
Try this:
Startup.Auth.cs
app.CreatePerOwinContext<ApplicationDbContext>(ApplicationDbContext.Create);
ApplicationDbContext.cs
public static ApplicationDbContext Create(IdentityFactoryOptions<ApplicationDbContext> options, IOwinContext context)
{
// Do things here.
string choice = context.Request.Cookies...;
// Make sure that the cookie is correct.
return new ApplicationDbContext(connectionName);
}
What you can do is have a static class that uses a provider to produce the connection string.
You can then let the user select the connection and store it wherever it works for you. The provider will read the connection string and feed it to the context initializer.
This way you can set the provider before all this takes place. You can mock it if you want to for unit testing purposes.
I'm writing from my cellphone. I'll improve the answer with code samples when I'm in front of a desktop.
Update (I am now in front of a desktop):
The easiest way to implement this is to modify the context class by adding a constructor that takes a nameOrConnectionString parameter and having the Create() method call that constructor with a provider that gets you the connection string:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("aspNetIdentity", throwIfV1Schema: false)
{
}
public ApplicationDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext(ConnectionStringProvider.ConnectionString);
}
}
The ConnectionStringProvider class is a static class that holds the connection string for you:
public static class ConnectionStringProvider
{
public static string ConnectionString { get; set; }
}
You can then set the connection string upon startup to a default and let the user change it. Upon change, store in this property.
Mind you, this is a simplistic answer meant to indicate a possible solution. Keep in mind that since the class is static, only one instance will exist and the connection string will be served to all callers so you need to account for that with concepts like dependency injection and session/user tracking.
For instance, the class could use an internal provider that is managed by a Dependency Resolver. You then let the DI container manage the scope of the provider.
Related
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);
}
}
I have setup .net core project and db context also. But i cant start using dbContext yet due this error-
"there is no argument given that corresponds to the required formal
parameter 'options'"
Controller:
public IActionResult Index()
{
using (var db = new BlexzWebDb())
{
}
return View();
}
Dbcontext Code:
public class BlexzWebDb : DbContext
{
public BlexzWebDb(DbContextOptions<BlexzWebDb> options)
: base(options)
{ }
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<AssignedRole> AssignedRoles { get; set; }
}
error picture attached. How can this issue be fixed?
Instantiate new object of DbContext from ConnectionString
var connectionstring = "Connection string";
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(connectionstring);
ApplicationDbContext dbContext = new ApplicationDbContext(optionsBuilder.Options);
// Or you can also instantiate inside using
using(ApplicationDbContext dbContext = new ApplicationDbContext(optionsBuilder.Options))
{
//...do stuff
}
Note
At the time of writing the use of EF Core with the Dependency injection framework wasn't as known as it is now. This answers gives answer to the question from a DI perspective, which at the time, helped out OP.
The other answer provides you a conventional way to instantiate the DbContext using the new operator.
TL;DR, 3 options:
Option 1
Register the DbContext during application configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextPool<BlexzWebDb>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BlexzWebConnection")));
}
and use the DI framework to retrieve it:
public class SomeController : Controller
{
private readonly BlexzWebDb _db;
//the framework handles this
public SomeController(BlexzWebDb db)
{
_db = db;
}
}
Option 2
If you are looking for a design-time IdentityDbContext using IOptions<OperationalStoreOptions>, see: Add migration for ApiAuthorizationDbContext from another project - EF Core
Option 3
Or use the new operator and provide the details, see #Qamar Zaman's answer for details.
The long answer, and why DI is a treat
In EF Core it's common to pass some DbContextOptions to the constructor.
So in general, a constructor looks like this:
public BlexzWebDb(DbContextOptions<BlexzWebDb> options) : base(options)
As you can see there, there is no valid overload in the form of a parameter-less constructor:
Thus, this does not work:
using (var db = new BlexzWebDb())
Obviously, you can pass in an Option object in the constructor but there is an alternative. So,
Instead
.Net Core has IoC implemented in it's roots. Okay, this means; you don't create a context, you ask the framework to give you one, based on some rules you defined before.
Example: somewhere you will register your dbcontext, (Startup.cs):
//typical configuration part of .net core
public void ConfigureServices(IServiceCollection services)
{
//some mvc
services.AddMvc();
//hey, options!
services.AddDbContextPool<BlexzWebDb>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BlexzWebConnection")));
//...etc
Now the registering part is done, you can retrieve your context from the framework. E.g.: inversion of control through a constructor in your controller:
public class SomeController : Controller
{
private readonly BlexzWebDb _db;
//the framework handles this
public SomeController(BlexzWebDb db)
{
_db = db;
}
//etc.
why?
So, why not just provide the arguments and new it?
There is nothing wrong with the use of new - there are a lot of scenario's in which it works best.
But, Inversion Of Control is considered to be a good practice. When doing asp dotnet core you're likely to use it quite often because most libraries provide extension methods to use it. If you are not familiar with it, and your research allow it; you should definitely give it a try.
Therefore, instead of providing "just a way to instantiate" the object, I'll try to get you onto this track - inline with the framework. It will save you some hassle afterwards. Besides, otherwise "use an activator's CreateInstance" would just be as valid as an answer ;-)
Some links:
MSDN Fundamentals
MSDN Dependency Injection
Wikipedia Inversion Of Control
As addition of #Stefan's answer there is another way to achieve this. You can set db connection string in OnConfiguring method of DbContext class without adding DbContext service in startup.cs.
Setting.cs
public static class Setting
{
public static string ConnectionString { get; set; }
}
Startup.cs
Setting.ConnectionString = Configuration.GetSection("ConnectionStrings:BlexzDbConnection").Value;
BlexzWebDb.cs
public class BlexzWebDb : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(Setting.ConnectionString);
}
}
}
HomeController.cs
public class HomeController : Controller
{
private readonly BlexzWebDb db;
public HomeController()
{
this.db = new BlexzWebDb();
}
//etc.
Code sample for EF Core 3.1:
public class Test
{
private readonly IServiceProvider _serviceProvider;
public Test(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task<RequestResult> Handle(...)
{
await using var context = CreateContext();
...
}
private DocumentContext CreateContext()
{
var options = _serviceProvider.GetService<IOptions<DocumentContextOptions>>();
return new DocumentContext(options);
}
}
I am not at all sure what the underlying problem is that is causing this exception.
I am using ASP.NET MVC, with Unity.Mvc, and Entity Framework 6. I have the following code to register my repositories:
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
// container.LoadConfiguration();
// TODO: Register your types here
// container.RegisterType<IProductRepository, ProductRepository>();
container.RegisterType<IGenericRepository<Customer>, GenericRepository<Customer>>();
container.RegisterType<IGenericRepository<Product>, GenericRepository<Product>>();
container.RegisterType<IGenericRepository<Order>, GenericRepository<Order>>();
container.RegisterType<IGenericRepository<OrderItem>, GenericRepository<OrderItem>>();
container.RegisterType<IGenericRepository<Supplier>, GenericRepository<Supplier>>();
}
And then in a controller I have:
public class IndexController : Controller
{
public IndexController(IGenericRepository<Customer> testGenericRepository)
{
var result = testGenericRepository.SelectAll();
}
public ActionResult Index()
{
return View();
}
}
And the repository has the following code:
public class GenericRepository<T> : IGenericRepository<T>
where T : class
{
private readonly DbContext _dbContext;
private readonly IDbSet<T> _dbSet;
public GenericRepository(DbContext dbContext)
{
if (dbContext == null)
{
throw new ArgumentNullException(nameof(dbContext));
}
_dbContext = dbContext;
_dbSet = _dbContext.Set<T>();
}
public IEnumerable<T> SelectAll()
{
return _dbSet.AsEnumerable<T>();
}
}
The problem that I'm having is that if I have a breakpoint in the "RegisterTypes" method, I can see that the container is definitely getting all the repositories registered, but a breakpoint in the constructor of the repositories never gets hit.
So I think that the fact that the breakpoint does not get hit, and I have not registered a "System.Data.Common.DbConnection" means that the DbContext that the repository uses never gets set.
I can't find any useful information about how to use "System.Data.Common.DbConnection" with Unity and the DbContext from Entity Framework.
How do I resolve this?
You should add to your RegisterTypes how to build your DbContext, and probably with which lifetime.
If you have your own class (say CustomContext) inheriting from DbContext, register it. Supposing your default lifetime is adequate:
container.RegisterType<DBContext, CustomContext>();
If you use directly DbContext, instruct Unity which constructor it should use. By example, supposing your connection string is named appConnectionString:
container.RegisterType<DBContext>(
new InjectionConstructor("name=appConnectionString"));
I'm trying to register IDbContext in Unity but I'm getting this
error
"The type IDbContext does not have an accessible constructor."
UnityConfig.cs
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IProductServices, ProductServices>();
container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
//---all ERROR below---
//container.RegisterType(typeof(IDbContext), typeof(DbContext));
//container.RegisterType<IDbContext>();
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ApplicationContext"].ConnectionString;
container.RegisterType(typeof(IDbContext), typeof(DbContext), new ContainerControlledLifetimeManager(), new InjectionConstructor(connectionString));
}
IDbContext.cs
public interface IDbContext
{
IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity;
int SaveChanges();
}
ApplicationContext.cs
public partial class ApplicationContext : DbContext, IDbContext
{
public ApplicationContext()
: this("name=ApplicationContext")
{
}
public ApplicationContext(string name)
: base(name)
{
}
public DbSet<Product> Products { get; set; }
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
}
You have 2 constructor in your DbContext therefore Unity can not initialize it simply remove ApplicationContext(string name) constructor an let default exist.
public partial class ApplicationContext : DbContext, IDbContext
{
public ApplicationContext()
: this("name=ApplicationContext")
{
}
public DbSet<Product> Products { get; set; }
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
}
An in unity side write:
container.RegisterType<IDbContext, ApplicationContext>();
But if you want keep your constructors try this:
container.RegisterType<IDbContext, ApplicationContext>(
new InjectionConstructor("YourConnectionStringYouProvidedSomewhere"));
So you could do something like this:
public class MyContext : DbContext
{
public MyContext() : base("connectionstringproperty") { }
}
public interface IMyContextFactory : IDbContextFactory<MyContext>
{
}
public class MyContextFactory : IMyContextFactory
{
public MyContext Create()
{
return new MyContext();
}
}
Register Like this:
container.RegisterType<IMyContextFactory, MyContextFactory>(new PerResolveLifetime());
And use in class like so:
public class UserService
{
IMyContextFactory contextFactory
public UserService(IMyContextFactory contextFactory)
{
contextFactory = contextFactory;
}
public List<User> GetUsers()
{
using(var context = this.contextFactory.Create())
{
return context.Users.ToList();
}
}
}
I don't use repositories until I really have to (which is nearly never :) ). But if UserService was a repository class instead (it kinda is at the moment) the implementation would be the same using constructor injection.
DbContext
If you want testability, EF 6 did a good job in making all methods and properties virtual so you could override them rather than creating an interface to back it.
For dependency injection, EF created IDbContextFactory<T> where T : DbContext
You should be inheriting from DbContext so that you can create some tables in the database. When you inherit you can create different constructors depending on your needs, the easiest I find is to create a parameterless one and call the base constructor overload which takes the web.config connection string property.
DbContext does not have a public, default constructor. The most minimial public constructor is one which takes a string value containing the name of the connection string to use (defined in your app.config or web.config file) or the actual connection string to the database itself.
If you have such a connection string defined in your app.config or web.config (or you have a suitable default connection string to be used throughout your application), try the following Unity registration:
// In the code below, "connection_string" is either the full database
// connection string or the name of the connection string as defined
// in the app.config or web.config files
//
// NOTE: This method requires a LifetimeManager.
// I used the default "singleton container" provided by unity.
// This may not be appropriate for your application--please use an
// appropriate container lifetime manager.
container.RegisterType(typeof(IDbContext), typeof(DbContext),
new ContainerControlledLifetimeManager(),
new InjectionConstructor("connection_string"));
If you have multiple databases you will need to connect to, then you may want to subclass DbContext (e.g. MyDB1DbContext, MyDB2DbContext) or use named registrations. If you subclass DbContext, then you could have the subclasses implement a public default constructor, passing a suitable connection string to the base DbContext class--at which point, your context registrations become simpler.
For one application-wide DbContext though, I'd probably just use the code snippet above.
Update after OP's Update
Unless you'll need to use multiple connection strings with your application-defined DbContext, I would simply define as follows:
public class ApplicationDbContext() : System.Data.Entity.DbContext
{
public ApplicationDbContext() : base("my_connection_string_name")
{ }
... // Rest of context class definition
}
Then, to register:
// NOTE: Can also use the generic version--may need to also change the
// lifetime management of the context depending on your application.
container.Register(typeof(IDbContext), typeof(ApplicationDbContext),
new ContainerControlledLifetimeManager());
I’m fairly new to IoC and perhaps my understanding of generics and inheritance is not strong enough for what I’m trying to do. You might find this to be a mess. I have a generic Repository<TEntity> base class:
public class Repository<TEntity> where TEntity : class, IEntity
{
private Table<TEntity> EntityTable;
private string _connectionString;
private string _userName;
public string UserName
{
get { return _userName; }
set { _userName = value; }
}
public Repository() {}
public Repository(string connectionString)
{
_connectionString = connectionString;
EntityTable = (new DataContext(connectionString)).GetTable<TEntity>();
}
public Repository(string connectionString, string userName)
{
_connectionString = connectionString;
_userName = userName;
EntityTable = (new DataContext(connectionString)).GetTable<TEntity>();
}
// Data access methods ...
... }
and a SqlClientRepository that inherits Repository:
public class SqlClientRepository : Repository<Client>
{
private Table<Client> ClientTable;
private string _connectionString;
private string _userName;
public SqlClientRepository() {}
public SqlClientRepository(string connectionString) : base(connectionString)
{
_connectionString = connectionString;
ClientTable = (new DataContext(connectionString)).GetTable<Client>();
}
public SqlClientRepository(string connectionString, string userName)
: base(connectionString, userName)
{
_connectionString = connectionString;
_userName = userName;
ClientTable = (new DataContext(connectionString)).GetTable<Client>();
}
// data access methods unique to Client repository
... }
The Repository class provides some generics methods like Save<TEntity>, Delete<TEntity>, etc, that I want all my repository derived classes to share.
The TEntity parameter is constrained to the IEntity interface:
public interface IEntity
{
int Id { get; set; }
NameValueCollection GetSaveRuleViolations();
NameValueCollection GetDeleteRuleViolations();
}
This allows the Repository class to reference these methods within its Save and Delete methods. Unit tests work fine on mock SqlClientRepository instances as well as live unit tests on the real database. However, in the MVC context:
public class ClientController : Controller
{
private SqlClientRepository _clientRepository;
public ClientController(SqlClientRepository clientRepository)
{
this._clientRepository = clientRepository;
}
public ClientController() { }
// ViewResult methods ...
... }
... _clientRepository is always null. I’m using Windor Castle as an IoC container. Here is the configuration:
<component id="ClientRepository" service="DomainModel.Concrete.Repository`1[[DomainModel.Entities.Client, DomainModel]], DomainModel"
type="DomainModel.Concrete.SqlClientRepository, DomainModel" lifestyle="PerWebRequest">
<parameters>
<connectionString>#{myConnStr}</connectionString>
</parameters>
</component>
I’ve tried many variations in the Windsor configuration file. I suspect it’s more of a design flaw in the above code. As I'm looking over my code, it occurs to me that when registering components with an IoC container, perhaps service must always be an interface. Could this be it? Does anybody have a suggestion? Thanks in advance.
---- AMENDMENT ----
In response to Answer 1, I’ve appended a new code sample, since it won’t format properly in the comment sections below.
I can get this to work:
public class ClientController : Controller
{
private IClientRepository _clientRepository;
public ClientController(IClientRepository clientRepository) { ... }
}
public interface IClientRepository : IRepository<Client> { ... }
public class SqlClientRepository : IClientRepository { ... }
... but now I’m required to duplicate my Save and Delete methods inside of SqlClientRepository and the benefits of my generic Repository class are lost. Once I try and have SqlClientRepository inherit from Repository<Client> again, like this:
public class SqlClientRepository : Repository<Client>, IClientRepository { ... }
... my null value for _clientRepository in the controller returns. Is there a way I can do this or is it not possible? I feel like I’ve tried many variations and can’t get it right.
Thanks again for your help.
No, service does not have to be an interface, although usually it should. Service is what a component exposes to an outside world (see the doco, let me know if it makes sense).
So, constructor of your ClientController says "I depend on SqlClientRepository service" That's bad (because services should be abstract (preferably interfaces)), but that's besides the point.
The component you're registering ( named ClientRepository), you're registering as Repository<Client> service.
Hopefully by now you're seeing what the problem is. ClientComponent expects SqlClientRepository, but the component you have registered does not expose itself as SqlClientRepository, but as Repository<Client>, hence Windsor thinks: "Ok, since I have no service for SqlClientRepository I'm gonna use the other constructor, to create ClientController" (see the doco for explanation on how Windsor picks which constructor to use)