I have a repository class that has a constructor with string parameter argument. It is a connection string. I created an interface for it and I'm using Unity in my UI project.
My question is, how do I set this up the 'right' way so that Unity will know how to properly construct my class in order to inject it when instantiating my controller?
I currently 'worked around' this by using a parameterless constructor for my repository but feel like this is a cop out.
Here is my repository constructor I want to use...
public CobraLettersRepository(string dbConnectionString)
{
_connString = dbConnectionString ?? throw new ArgumentNullException(dbConnectionString);
dbConnection = new SqlConnection(_connString);
dbConnection.Open();
}
I created ICobraLettersRepository and want to inject it into my controller constructor.
public CobraLetterController(ICobraLetterRepository cobraLetterRepository)
{
if (cobraLetterRepository == null)
throw new ArgumentNullException(cobraLetterRepository);
_cobraLetterRepo = cobraLetterRepository;
}
When I try this, the code compiles but I get runtime errors whenever I attempt to navigate to a part of my app where those controller methods are called.
I would say to encapsulate the Connection String inside a configuration object and put the get of the connection string inside the constructor of that configuration object.
Then just register your configuration object as other class.
public class ProjectConfiguration
{
public string ConnectionString { get; set; }
public ProjectConfiguration()
{
ConnectionString = //Get the configuration from , i.e: ConfigurationManager
}
}
And then, inject it :
public CobraLettersRepository(ProjectConfiguration configuration)
{
if(configuration == null){
throw new ArgumentNullException(configuration);
}
_connString = configuration.ConnectionString;
dbConnection = new SqlConnection(_connString);
dbConnection.Open();
}
With that you can always isolate the config from the DI container and leave the injection as simple as you can. Also, you can get the config from your config files (one per environment).
EDIT: Also check your repository constructor logic, maybe you don't want to Open the connection on the constructor. Is better to make a using statement on your class methods like :
using(SqlConnection connection = new SqlConnection(_injectedConfiguration.ConnectionString)){
connection.Open();
//Make your stuff here
}
With the using you ensure that the connection will be correctly disposed.
Note that with the configuration object approach you can just store the configuration object in a private readonly variable and use it whatever you need.
During registration use an InjectionConstructor to pass the connection string name. You need to remove all constructors except one that expects a string argument.
container.RegisterType<ICobraLetterRepository,CobraLettersRepository>(new InjectionConstructor("connectionstringname"));
Please note that it will lead to problems somewhere/sometime. You need to change the logic to create the database connection (you may refer to Breaking SOLID Principles in multiple implementation of an Interface or Resolve named registration dependency in Unity with runtime parameter).
Related
I'm writing an MVC C# application. I use dapper as a lightweight ORM. My connection strings are defined with server and initial catalog, and currently if I need to access a different database I define another connection string, and use Ninject bindings to use a particular connection string based on the manager i'm injecting it into, like so:
public class NinjectBindings : NinjectModule
{
public override void Load()
{
Bind<IDbConnection>().To<SqlConnection>()
.WhenInjectedInto<DashboardManager>()
.InRequestScope()
.Named("myDashboard")
.WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["dbDashboard"].ConnectionString);
Bind<IDbConnection>().To<SqlConnection>()
.WhenInjectedInto<ScoreboardManager>()
.InRequestScope()
.Named("myScoreboard")
.WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["dbScoreboard"].ConnectionString);
}
}
Unfortunately this doesn't work if I have code in the same Manager that needs to call stored procedures that are on different databases than the initially specified catalog.
Question is: Can I just define one connection string, lose all the ninject binding stuff above, and simply change the Initial Catalog to a point to a different database on the fly?
Do you need both Named and WhenInjectedInto constraints for your bindings?
I believe you have a class that requires both connectionstrings, this could be achieved using Named binding:
Bind<IDbConnection>().To<SqlConnection>()
.InRequestScope()
.Named("myDashboard")
.WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["dbDashboard"].ConnectionString);
Bind<IDbConnection>().To<SqlConnection>()
.InRequestScope()
.Named("myScoreboard")
.WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["dbScoreboard"].ConnectionString);
And your class can get both connections:
public class ClassWith2DbDependency // <-- I would question this class for SRP violation
{
private readonly IDbConnection _dashboardConnection;
private readonly IDbConnection _scoreboardConnection;
public ClassWith2DBDependency(
[Named("myDashboard")] IDbConnection dashboardConnection
[Named("myScoreboard")] IDbConnection scoreboardConnection)
{
_dashboardConnection = dashboardConnection;
_scoreboardConnection = scoreboardConnection;
}
public void WriteTo2Dbs()
{
// execute dashboard DB procedure
// execute scoreboard DB procedure
}
}
Can I just define one connection string, lose all the ninject binding
stuff above, and simply change the Initial Catalog to a point to a
different database on the fly?
Changing Initial Catalog doesn't affect an existing SqlConnection. It is possible to manage the dependencies yourself, but you still need 2 connectionstrings:
public class ClassWith2DbDependency
{
public void WriteTo2Dbs()
{
var dashboardCon = ConfigurationManager.ConnectionStrings["dbDashboard"].ConnectionString;
using (SqlConnection connection = new SqlConnection(dashboardCon))
{
// execute dashboard DB procedure
}
var scoreboardCon = ConfigurationManager.ConnectionStrings["dbScoreboard"].ConnectionString;
using (SqlConnection connection = new SqlConnection(scoreboardCon))
{
// execute scoreboard DB procedure
}
}
}
However, I do NOT recommend this approach, the above class violates DI principle, by having Opaque Dependencies.
I haven't seen your code, but it doesn't sound like you are using Repository Pattern? This could be a good option...
I have an application that uses Structuremap for DI for both my business and DAL layer. Up to this point, I have had a single DAL per environment that I have been working on. So I would grab it from the config and use that value for all my connections. An example of this is.
using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["Database"].ToString()))
{
//Do a call to db here.
}
I am calling this method using structure map as follows.
ObjectFactory.GetInstance<IDALManager>().MethodName();
Now I have a new feature where I want to allow the users to make change in a dev environment and then push a button to elevate it to test or prod environment. Therefore my connectionstring for the DAL manager will need to be able to change. I also would like to keep all the connection string access in the DAL and not in the other layers. I am looking for advice on how to do this or what design patterns to look into for this.
UPDATED INFORMATION
The user will determine which connection string needs to be used. For example, they will be moving data from dev to test, they will select a source and a destination.
string source = \\user selection from combobox.
if (source == "DEV")
{
//Instantiate dev instance of manager
}
if (source == "TEST")
{
//Instantiate Test Instance of manager.
}
You need an abstract factory. Take a look at the answer in this question for some examples.
In your particular case, your abstract factory interface should look like this:
public interface IDALManagerFactory
{
IDALManager Create(string environment);
}
You need to create an implementation of this interface that creates a "DAL Manager" with the appropriate connection string.
To be able to do this, you need the connection string to be injected into the constructor of your class like this:
public class MyDalManager: IDALManager
{
private readonly string connectionString;
public MyDalManager(string connectionString)
{
this.connectionString = connectionString;
}
public MyMethod()
{
//..
using (SqlConnection con = new SqlConnection(connectionString))
{
//Do a call to db here.
}
}
}
Now the implementation of the factory would look something like this:
public class DALManagerFactory : IDALManagerFactory
{
public IDALManager Create(string environment)
{
if(environment == "DEV")
return new MyDalManager(
ConfigurationManager.ConnectionStrings["Database"].ToString());
//...
}
}
This factory class should live in the Composition Root. You can also access the container inside this factory class to create the "DAL Manager".
Now, the class that needs access to the appropriate "DAL Manager" should have a IDALManagerFactory injected into its constructor, and it would use such factory to create a IDALManager by invoking the Create method passing the environment name.
Please note that in your code, you are accessing the connection string in the DAL layer. You really should access such information in the composition root only.
Consider the following example:
public class CommunicationClient : IClient
{
public CommunicationClient(IServerSettings settings) { ... }
// Code
}
public class SettingsManager : ISettingsManager
{
SettingsManager(IDbSettingManager manager)
// Code
public IDictionary<string, string> GetSettings() { ... }
}
Problem:
While performing registrations (using SimpleInjector), I need to provide values that are obtained from an instance of SetingsManager and fill ServerSettings instance (concrete type for IServerSettings) but if I call GetInstance<ISettingsManager> before registering CommunicationClient, it gives me an error that I cannot do that
Error:
The container can't be changed after the first call to GetInstance, GetAllInstances and Verify.)
One solution could be to inject ISettingsManager as a dependency to CommunicationClient but I really don't want to pass it as it would provide more than required information to it.
EDIT: Container Registration
container.Register(typeof(ICommunicationClient), typeof(CommunicationClient));
ISettingsManager settingsManager = container.GetInstance<ISettingsManager>();
string url = settingsManager.GetSetting("url");
string userName = settingsManager.GetSetting("username");
string password = settingsManager.GetSetting("password");
container.Register(typeof(IServerConfiguration), () =>
new ServerConfiguration(url, userName, password);
Any suggestions/alternative solutions on how to achieve above in a cleaner way? Thanks.
Simple Injector locks the container for further changes after its first use. This is an explicit design choice, which is described here. This means that you can't call Register after you called GetInstance, but there should never be a reason to do this. Or in other words, your configuration can always be rewritten in a way that you don't need this. In your case your configuration will probably look something like this:
var settingsManager = new SettingsManager(new SqlSettingManager("connStr"));
container.RegisterSingle<ISettingsManager>(settingsManager);
container.Register<ICommunicationClient, CommunicationClient>();
string url = settingsManager.GetSetting("url");
string userName = settingsManager.GetSetting("username");
string password = settingsManager.GetSetting("password");
container.Register<IServerConfiguration>(() =>
new ServerConfiguration(url, userName, password));
There you see that SettingsManager is not built-up by the container. When using a DI container, you are not required to let the DI container build up every instance for you. Letting the container auto-wire instances for you is done to lower the maintenance burden of your Composition Root and makes it easier to apply cross-cutting concerns (using decorators for instance) to groups of related classes. In the case of the SettingsManager and SqlSettingsManager classes, it is very unlikely that their constructor will change that often that it will increase the maintenance burden of your Composition Root. It's therefore perfectly fine to manually create those instances once.
If I understand correctly, to create your CommunicationClient class, you need to pass information that are retrieved by calling a method on an instance of your ISettingsManager, but you don't want to pass the ISettingsManager as a dependency to your CommunicationClient?
One solution for that would be to create, and register, a factory that would have a dependency on ISettingsManager and that would have a CreateClient method that would return the configured client.
public class CommunicationClientFactory : ICommunicationClientFactory
{
public CommunicationClientFactory(ISettingsManager settingsManager) {...}
public CreateClient() {...}
}
This way your CommunicationClient is not dependent on the ISettingsManager and you have just this factory that does the work of creating your instance.
Edit:
An alternative, if you don't want to create a factory for this, would be to have your CommunicationClient object be created in an "invalid" state, and have a method that would set the settings and make its state valid.
Something like:
public class CommunicationClient : IClient
{
public CommunicationClient() { ... }
// Code
CommunicationClient WithSettings(IServerSettings settings) { ... }
}
Of course, then you'd have to make sure that the user don't use it when the settings have not been passed yet, potentially sending an exception if that would be the case. I like this solution less, because it's less explicit that you NEED those settings to have your object in a correct state.
I am building an MVC application that connect to diferent databases depending on the user that has log in.
For this i have 3 projects DAL using entity framework(DataBaseFirst) where i have extended the dbcontext so that i can pass the connectionstring like this:
public partial class ARACultivoEntities
{
public ARACultivoEntities(string nameOfConnectionString)
: base(nameOfConnectionString)
{
}
}
Note: I have the connections strings defined in the web.config of the mvc project.
There is also another project, Services where i have a genericService from where other service can inherit this like this:
public class GenericService<T> : IGenericService<T>
where T : class
{
protected ARACultivoEntities Db;
protected DbSet<T> Table;
public GenericService(string nameConnectionString)
{
if (string.IsNullOrEmpty(nameConnectionString))
{
throw new ArgumentNullException("nameConnectionString");
}
Db = new ARACultivoEntities(nameConnectionString);
Table = Db.Set<T>();
}
Now i save the name of the connection string in the user claims when he logs in and in the controllers i have something like this:
public class DeduccionController : Controller
{
private IGenericService<Deducciones> service;
public DeduccionController()
{
service = ServiceFactoryGeneric<Deducciones>.InitGenericService(GetClaimsUser.Cadena);
//GetClaimsUser.Cadena has the name of the connectionString
//ServiceFactoryGeneric<Deducciones>.InitGenericService do this:
// return new GenericService<T>(connectionString);
}
now i want to instead of having my own factories i want to use an Ioc Container and i have chosen unity for this, i am new to this, i've read some articles and i think i undsertand the basics but i dont know how to pass the connection string after the user has log in because my RegisterTypes hapen at the application start
public static void RegisterTypes(IUnityContainer container)
{
// this happen at application start
// string nameOfConnectionString = *user is still not loged in*
container.RegisterType<IGenericService<T>, GenericService<T>>(
new InjectionConstructor(nameOfConnectionString));
}
i been thinkin to try to tweet the code to register my types after the user has loged in but i dont think this a good idea..
i also have been thinking about adding a public method to my IGenericService so that i can set my connectionString after the service is constructed and implemented something like this:
public void SetConnectionString(string nameOfConnectionString)
{
Db.Database.Connection.ConnectionString = nameOfConnectionString;
//not sure if this actually works
}
then my controller will be something like this:
public class DeduccionController : Controller
{
private IGenericService<Deducciones> _service;
public DeduccionController(IGenericService<Deducciones> service)
{
_service = service;
_service.SetConnectionString(GetUserClaims.Cadena);
}
and let my RegisterTypes just with the:
container.RegisterType<IGenericService<T>, GenericService<T>>()
but since i new to this world of IoCs i am not sure if this is the best way
What would be the correct way to do this using Unity?
Thank you for reading.
I am sorry for my english not my first languague.
I recently had to do something similar by swapping connection strings based on a route parameter specifying a geo-location.
I would recommend building your own Unity LifetimeManager that acts in a instance per session scope. Register an object that acts as a configuration container for the connection string property.
[See Unity Lifetime Manager: http://msdn.microsoft.com/en-us/library/microsoft.practices.unity.lifetimemanager(v=pandp.30).aspx]
Then you could inject that singleton instance of this configuration object into your controller and set the connection string property once a user has logged in. You could then inject that same singleton instance into a DbContext factory that instantiates your DbContext using the connection string specified in your configuration object.
Like I said, it may not be the most elegant solution, but I liked it better than having to pass a connection string through the many tiers of your application stack. Hope this helps.
Using vs2012, I've got a Test Unit project (for testing a Service) that incorporates an .edmx file and linq. The edmx is created at design time and I have created an object (called Store.Data.Common) that retrieves the connection string from the App.Config file (decrypts the string and builds the entire string including the meta data):
//Object is called Store.Data.Common
public static string GetConnectionString(string databaseName)
{
var security = new Security();
var connectionString = security.GetDecoded(ConfigurationManager.ConnectionStrings[databaseName+"Encrypted"].ToString(), 0);
var environment = ConfigurationManager.AppSettings["Environment"].ToString();
var dataSource = security.GetDecoded(ConfigurationManager.AppSettings[environment], 0);
connectionString = string.Format(connectionString, dataSource);
return connectionString;
}
I've also modified the .tt files to include an overload of the constructor to call this method to build the connection string, like so:
//Original Constructor which I modified and added the parameter to pass to the other constructor.
public StoreContext()
: base("name=StoreContext")
{
}
//Constructor I added:
public StoreContext(string connection)
{
Store.Data.Common.ConnectionBuilder.GetConnectionString("StoreContext");
}
Everything builds correctly, however, when I try to new-up an object for the StoreContext and leave the constructor empty, it never gets to the second constructor:
StoreContext storeContext = new StoreContext();
When I debug this test and walk through it, it only gets to the first constructor and that's it. Obviously, if I do something like this:
StoreContext storeContext = new StoreContext("Blah");
Then it goes to the second one as expected....by my question is, why doesn't the first method work when passing nothing to the constructor? Technically it should work, right?
I think you mean to use
public StoreContext()
: this("name=StoreContext")
{
}
(using this rather than base).
using this() means you're calling a constructor on the same class. When you say base() you're trying to call a constructor on the base class.
Edit: It also doesn't look like you're using the parameter you're passing into that non-default constructor. But that's another issue, not the root of your problem.