Just starting playing with the .Net Core RC2 by migrating a current MVC .Net app I developed. It looks like to me because of the way that configuration is handled with appsettings.json that if I have multiple connection strings I either have to use EF to retrieve a connectionstring or I have to create separate classes named for each connection string. All the examples I see either use EF (which doesn't make sense for me since I will be using Dapper) or the example builds a class named after the section in the config. Am I missing a better solution?
"Data": {
"Server1": {
"ConnectionString": "data source={server1};initial catalog=master;integrated security=True;"
},
"Server2": {
"ConnectionString": "data source={server2};initial catalog=master;integrated security=True;"
}
}
Why would I want to build two classes, one named "Server1" and another "Server2" if the only property each had was a connectionstring?
There are a couple of corrections that I made to Adem's response to work with RC2, so I figured I better post them.
I configured the appsettings.json and created a class like Adem's
{
"ConnectionStrings": {
"DefaultConnectionString": "Default",
"CustomConnectionString": "Custom"
}
}
and
public class ConnectionStrings
{
public string DefaultConnectionString { get; set; }
public string CustomConnectionString { get; set; }
}
most of Adem's code comes out of the box in VS for RC2, so I just added the line below to the ConfigureServices method
services.Configure<Models.ConnectionStrings>(Configuration.GetSection("ConnectionStrings"));
The main missing point is that the connection string has to be passed to the controller (Once you’ve specified a strongly-typed configuration object and added it to the services collection, you can request it from any Controller or Action method by requesting an instance of IOptions, https://docs.asp.net/en/latest/mvc/controllers/dependency-injection.html)
So this goes to the controller,
private readonly ConnectionStrings _connectionStrings;
public HomeController(IOptions<ConnectionStrings> connectionStrings)
{
_connectionStrings = connectionStrings.Value;
}
and then when you instantiate the DAL you pass the appropriate connectionString
DAL.DataMethods dm = new DAL.DataMethods(_connectionStrings.CustomConnectionString);
All the examples show this, they just don't state it, why my attempts to pull directly from the DAL didn't work
I don't like the idea of instantiating the DAL. Rather, I'd do something like this
public class ConnectionStrings : Dictionary<string, string> { }
And something like this in the ctor of the DAL
public Dal(IOptionsMonitor<ConnectionStrings> optionsAccessor, ILogger<Dal> logger)
{
_connections = optionsAccessor.CurrentValue;
_logger = logger;
}
and you'll need to register with IoC
services.Configure<ConnectionStrings>(configuration.GetSection("ConnectionStrings")); /* services is the IServiceCollection */
Now you have all the connection strings in the DAL object. You can use them on each query or even select it by index on every call.
You can use Options to access in DAL layer. I will try to write simple example(RC1):
First you need to create appsettings.json file with below content:
{
"ConnectionStrings": {
"DefaultConnectionString": "Default",
"CustomConnectionString": "Custom"
}
}
Then create a class:
public class ConnectionStrings
{
public string DefaultConnectionString { get; set; }
public string CustomConnectionString { get; set; }
}
And in Startup.cs
private IConfiguration Configuration;
public Startup(IApplicationEnvironment app)
{
var builder = new ConfigurationBuilder()
.SetBasePath(app.ApplicationBasePath)
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
// ....
services.AddOptions();
services.Configure<ConnectionStrings>(Configuration.GetSection("ConnectionStrings"));
}
Finally inject it in the DAL class:
private IOptions<ConnectionStrings> _connectionStrings;
public DalClass(IOptions<ConnectionStrings> connectionStrings)
{
_connectionStrings = connectionStrings;
}
//use it
Related
I have a vNext console application where one of my classes takes the OptionsModel<T> POCO configuration settings class.
I am unable to get the POCO settings class resolved and injected into my RabbitMqConnection class below.
Setting up the ServiceProvider is not the issue, it's the resolution of the settings class.
Note, this is a vNext console application (not an mvc6 app).
My second question is, and I understand constructor arguments should be kept minimal, but is it not best to pass the two strings as constructor arguments rather than an IOptions class as the former method is more descriptive of what the RabbitMqConnection class requires? If so, how is this best injected from the class that defined the mappings (Program.cs file in example below)
public class RabbitMqConnection
{
public string HostName { get; set; }
public string UserName { get; set; }
public RabbitMqConnection(IOptions<MessagingSettings> settings)
{
HostName = settings.Value.HostName;
UserName = settings.Value.UserName;
}
}
public class MessagingSettings
{
public string HostName { get; set; }
public string UserName { get; set; }
}
appsettings.json
{
"MessagingSettings":{
"HostName":"localhost",
"Username":"guest"
}
}
public void ConfigureServices(IServiceCollection services)
{
// tried registration a number of ways as below
services.Configure<MessagingSettings>(Configuration.GetSection("MessagingSettings"));
services.Configure<MessagingSettings>(Configuration);
// attempt 1 - get runtime error saying cant resolve IOptions<MessageSettings>
services.TryAdd(ServiceDescriptor.Singleton<RabbitMqConnection, RabbitMqConnection>());
// attempt 2 - same as above, when i breakpoint on messagingSettings, all the values in the object are null
services.TryAdd(ServiceDescriptor.Singleton<RabbitMqConnection>(factory =>
{
// instead of injecting the MessageSettings, pass through the string values (constructor omitted for clarity)
var messagingSettings = Configuration.Get<MessagingSettings>();
return new RabbitMqConnection(messagingSettings.HostName, messagingSettings.UserName);
}));
}
var conn = ServiceProvider.GetRequiredService<RabbitMqConnection>();
You need to call services.AddOptions()
When using WebApi 2 my web.config was
<connectionStrings>
<add name="RavenHQ" connectionString="Url=http://localhost:8080;Database=ModelFarmDb" />
</connectionStrings>
For ASP.NET 5.0, I can't work out how to write the config.json file to do the same thing.
I've tried
{
"Data": {
"RavenHQ": {
ConnectionString: "Url=http://localhost:8080;Database=ModelFarmDb"
}
}
}
but it doesn't work. Any suggestions on how to directly map the web.config sections to config.json so as not to break other libraries that assume a web.config?
The app is running under IIS Express locally and is a web app on Azure.
Many thanks!
You accomplish this in asp.net 5.0 in a different way. I used json file for this example. If you need add xml file just use these package Microsoft.Framework.Configuration.Xml and use .AddXmlFile() method
This example uses beta 7
Create an AppSetting class
public class AppSetting
{
public string Setting1 { get; set; }
public string Setting2 { get; set; }
}
In your startup file add the json file with the configuration on this example is call config.json
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("config.json")
.AddXmlFile("thefilename")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfiguration Configuration { get; set; }
Then you need to add the app service your AppSetting class so it can be injected
for later use
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
Then in your controller or where ever you need the configuraton inject the IOptions<AppSettings>
public class SampleController : Controller
{
private readonly AppSettings _appSettings;
public SampleController(IOptions<AppSettings> appSettings)
{
_appSettings = appSettings.Value;
}
}
and this is how the json looks like
{
"AppSetting": {
"Setting1": "Foo1",
"Setting1": "Foo1"
}
}
I took these peaces of code from live.asp.net in github
As I understand a connection string is attached to one class only. But what if I have many Model classes? Can I use one connection string for multiple classes?
This is a simple version of my UserModel.cs file:
public class UserModel
{
public int Id { get; set; }
public string Email { get; set; }
}
public class UserTable : DbContext
{
public UserModel GetByEmail(string Email)
{
return this.Database.SqlQuery<UserModel>("SELECT * FROM Users WHERE Email=#Email", new SqlParameter("Email", Email)).SingleOrDefault();
}
}
And this is the connection string:
<connectionStrings>
<add name="UserModel"
connectionString="Server=.\SQLEXPRESS;Database=MyDatabase;User Id=MyUser;Password=MyPassword;"
providerName="System.Data.SqlClient" />
</connectionStrings>
Now lets say I want to add a new Model class named DataTable also derived from DbContext as user table is. Do I need a connection string named the same or can I use the already defined one? What is the conventional way of dealing with multiple Model classes and connection strings?
The DbContext class uses the ConnectionString to make the connection to the database.
You normally have multiple model classes exposed by a DbContext.
It is possible to have multiple DbContext objects that use the same connection string value to connect to the database. In this way, you can separate portions of your model into separate contexts if desired (for example, if you are creating separate assemblies that access different tables but provide similar services to the application).
One caveat to note with EF up to at least 5.0, you cannot use the code-first migrations with multiple DbContexts, one will overwrite the other's changes. The solution to this is to create an aggregated DbContext that is only used for the Migrations process.
I've done this in an app that I built. I used the Unity IoC container, and the built a Plugin Interface that allowed me to pass my ConnectionStringName into my separated DbContexts. An example of the plugin in one of the assemblies was:
public class Bootstrapper : IBootstrapper
{
public void Bootstrap(IUnityContainer container, string connectionStringName)
{
container.RegisterType<ISQService, SQService>();
container.RegisterType<ISQEntities, SQEntities>(
new HierarchicalLifetimeManager(), new InjectionConstructor(connectionStringName));
container.RegisterType<IController, SQController>("SQ");
}
}
My global.asax referenced the bootstrapper class below:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ModelBinders.Binders[typeof(DataTable)] = new DataTableModelBinder();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
Bootstrapper.Initialise();
}
protected void Application_End()
{
Bootstrapper.Dispose();
}
Bootstrapper
public static class Bootstrapper
{
private static IUnityContainer container;
public static void Initialise()
{
container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
public static void Dispose()
{
container.Dispose();
}
private static void RegisterPlugins(IUnityContainer theContainer, string wildcard, string connectionStringName)
{
var pluginBootStrappers = from Assembly assembly in wildcard.LoadAssemblies()
from type in assembly.GetExportedTypes()
where typeof(IBootstrapper).IsAssignableFrom(type)
select (IBootstrapper)Activator.CreateInstance(type);
pluginBootStrappers.ToList().ForEach(b => b.Bootstrap(theContainer, connectionStringName));
}
private static IUnityContainer BuildUnityContainer()
{
var theContainer = new UnityContainer();
const string ConnectionStringName = "MyDb";
RegisterPlugins(theContainer, "MyApp.Systems.*.dll", ConnectionStringName);
// Register Application Specific objects
theContainer.RegisterType<IMyEntities, MyEntities>(
new HierarchicalLifetimeManager(),
new InjectionConstructor(ConnectionStringName));
theContainer.RegisterType<IAimaService, AimaService>();
var factory = new UnityControllerFactory(theContainer);
ControllerBuilder.Current.SetControllerFactory(factory);
return theContainer;
}
}
The connection string defines the parameters needed to connect to the DB.
Maybe I think you are talking about or confusing the SQL Query with the connectionstring.
Yes one SQL Query can QUERY more than one table at any given time.
Maybe you could look into the "SQL Query Statement" on google for in depth information.
In a MVC3-application with Ninject.MVC 2.2.0.3 (after merge), instead of injecting repostories directly into controllers I'm trying to make a service-layer that contain the businesslogic and inject the repostories there. I pass the ninject-DependencyResolver to the service-layer as a dynamic object (since I don't want to reference mvc nor ninject there). Then I call GetService on it to get repositories with the bindings and lifetimes I specify in NinjectHttpApplicationModule. EDIT: In short, it failed.
How can the IoC-container be passed to the service-layer in this case? (Different approaches are also very welcome.)
EDIT: Here is an example to illustrate how I understand the answer and comments.
I should avoid the service locator (anti-)pattern and instead use dependency injection. So lets say I want to create an admin-site for Products and Categories in Northwind. I create models, repositories, services, controllers and views according to the table-definitions. The services call directly to the repositories at this point, no logic there. I have pillars of functionality and the views show raw data. These bindings are configured for NinjectMVC3:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICategoryRepository>().To<CategoryRepository>();
kernel.Bind<IProductRepository>().To<ProductRepository>();
}
Repository-instances are created by ninject via two layers of constructor injection, in the ProductController:
private readonly ProductsService _productsService;
public ProductController(ProductsService productsService)
{
// Trimmed for this post: nullchecks with throw ArgumentNullException
_productsService = productsService;
}
and ProductsService:
protected readonly IProductRepository _productRepository;
public ProductsService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
I have no need to decouple the services for now but have prepared for mocking the db.
To show a dropdown of categories in Product/Edit I make a ViewModel that holds the categories in addition to the Product:
public class ProductViewModel
{
public Product Product { get; set; }
public IEnumerable<Category> Categories { get; set; }
}
The ProductsService now needs a CategoriesRepository to create it.
private readonly ICategoryRepository _categoryRepository;
// Changed constructor to take the additional repository
public ProductsServiceEx(IProductRepository productRepository,
ICategoryRepository categoryRepository)
{
_productRepository = productRepository;
_categoryRepository = categoryRepository;
}
public ProductViewModel GetProductViewModel(int id)
{
return new ProductViewModel
{
Product = _productRepository.GetById(id),
Categories = _categoryRepository.GetAll().ToArray(),
};
}
I change the GET Edit-action to return View(_productsService.GetProductViewModel(id)); and the Edit-view to show a dropdown:
#model Northwind.BLL.ProductViewModel
...
#Html.DropDownListFor(pvm => pvm.Product.CategoryId, Model.Categories
.Select(c => new SelectListItem{Text = c.Name, Value = c.Id.ToString(), Selected = c.Id == Model.Product.CategoryId}))
One small problem with this, and the reason I went astray with Service Locator, is that none of the other action-methods in ProductController need the categories-repository. I feel it's a waste and not logical to create it unless needed. Am I missing something?
You don't need to pass the object around you can do something like this
// global.aspx
protected void Application_Start()
{
// Hook our DI stuff when application starts
SetupDependencyInjection();
}
public void SetupDependencyInjection()
{
// Tell ASP.NET MVC 3 to use our Ninject DI Container
DependencyResolver.SetResolver(new NinjectDependencyResolver(CreateKernel()));
}
protected IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new NhibernateModule(),
new ServiceModule(),
new RepoModule()
};
return new StandardKernel(modules);
}
So in this one I setup all the ninject stuff. I make a kernal with 3 files to split up all my binding so it is easy to find.
In my service layer class you just pass in the interfaces you want. This service class is in it's own project folder where I keep all my service layer classes and has no reference to the ninject library.
// service.cs
private readonly IRepo repo;
// constructor
public Service(IRepo repo)
{
this.repo = repo;
}
This is how my ServiceModule looks like(what is created in the global.aspx)
// ServiceModule()
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<IRepo>().To<Repo>();
}
}
Seee how I bind the interface to the repo. Now every time it see that interface it will automatically bind the the Repo class to it. So you don't need to pass the object around or anything.
You don't need worry about importing .dll into your service layer. For instance I have my service classes in their own project file and everything you see above(expect the service class of course) is in my webui project(where my views and global.aspx is).
Ninject does not care if the service is in a different project since I guess it is being referenced in the webui project.
Edit
Forgot to give you the NinjectDependecyResolver
public class NinjectDependencyResolver : IDependencyResolver
{
private readonly IResolutionRoot resolutionRoot;
public NinjectDependencyResolver(IResolutionRoot kernel)
{
resolutionRoot = kernel;
}
public object GetService(Type serviceType)
{
return resolutionRoot.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return resolutionRoot.GetAll(serviceType);
}
}
I'm a newbie when it comes to DI and ninject and I'm struggling a bit
about when the actual injection should happen and how to start the
binding.
I'm using it already in my web application and it working fine there,
but now I want to use injection in a class library.
Say I have a class like this:
public class TestClass
{
[Inject]
public IRoleRepository RoleRepository { get; set; }
[Inject]
public ISiteRepository SiteRepository { get; set; }
[Inject]
public IUserRepository UserRepository { get; set; }
private readonly string _fileName;
public TestClass(string fileName)
{
_fileName = fileName;
}
public void ImportData()
{
var user = UserRepository.GetByUserName("myname");
var role = RoleRepository.GetByRoleName("myname");
var site = SiteRepository.GetByID(15);
// Use file etc
}
}
I want to use property injection here because I need to pass in a
filename in my constructor. Am I correct in saying that if I need to
pass in a constructor parameter, I cannot use constructor injection?
If I can use constructor injection with additional parameters, how do
I pass those parameters in?
I have a console app that consumes by Test class that looks as
follows:
class Program
{
static void Main(string[] args)
{
// NinjectRepositoryModule Binds my IRoleRepository etc to concrete
// types and works fine as I'm using it in my web app without any
// problems
IKernel kernel = new StandardKernel(new NinjectRepositoryModule());
var test = new TestClass("filename");
test.ImportData();
}
}
My problem is that when I call test.ImportData() my repositories are null - nothing has been injected into them. I have tried creating another module and calling
Bind<TestClass>().ToSelf();
as I thought this might resolve all injection properties in TestClass but I'm getting nowhere.
I'm sure this is a trivial problem, but I just can't seem to find out
how to go about this.
You are directly newing TestClass, which Ninject has no way of intercepting - remember there's no magic like code transformation intercepting your news etc.
You should be doing kernel.Get<TestClass> instead.
Failing that, you can inject it after you new it with a kernel.Inject( test);
I think there's an article in the wiki that talks about Inject vs Get etc.
Note that in general, direct Get or Inject calls are a Doing It Wrong smell of Service Location, which is an antipattern. In the case of your web app, the NinjectHttpModule and PageBase are the hook that intercepts object creation - there are similar interceptors / logical places to intercept in other styles of app.
Re your Bind<TestClass>().ToSelf(), generally a StandardKernel has ImplicitSelfBinding = true which would make that unnecessary (unless you want to influence its Scope to be something other than .InTransientScope()).
A final style point:- you're using property injection. There are rarely good reasons for this, so you should be using constructor injection instead.
And do go buy Dependency Injection in .NET by #Mark Seemann, who has stacks of excellent posts around here which cover lots of important but subtle considerations in and around the Dependency Injection area.
OK,
I've found out how to do what I need, thanks in part to your comments Ruben. I've created a new module that basically holds the configuration that I use in the class library. Within this module I can either Bind using a placeholder Interface or I can add a constructor parameter to the CustomerLoader.
Below is the code from a dummy console app to demonstrating both ways.
This might help someone else getting started with Ninject!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Core;
using Ninject.Core.Behavior;
namespace NinjectTest
{
public class Program
{
public static void Main(string[] args)
{
var kernel = new StandardKernel(new RepositoryModule(), new ProgramModule());
var loader = kernel.Get<CustomerLoader>();
loader.LoadCustomer();
Console.ReadKey();
}
}
public class ProgramModule : StandardModule
{
public override void Load()
{
// To get ninject to add the constructor parameter uncomment the line below
//Bind<CustomerLoader>().ToSelf().WithArgument("fileName", "string argument file name");
Bind<LiveFileName>().To<LiveFileName>();
}
}
public class RepositoryModule : StandardModule
{
public override void Load()
{
Bind<ICustomerRepository>().To<CustomerRepository>().Using<SingletonBehavior>();
}
}
public interface IFileNameContainer
{
string FileName { get; }
}
public class LiveFileName : IFileNameContainer
{
public string FileName
{
get { return "live file name"; }
}
}
public class CustomerLoader
{
[Inject]
public ICustomerRepository CustomerRepository { get; set; }
private string _fileName;
// To get ninject to add the constructor parameter uncomment the line below
//public CustomerLoader(string fileName)
//{
// _fileName = fileName;
//}
public CustomerLoader(IFileNameContainer fileNameContainer)
{
_fileName = fileNameContainer.FileName;
}
public void LoadCustomer()
{
Customer c = CustomerRepository.GetCustomer();
Console.WriteLine(string.Format("Name:{0}\nAge:{1}\nFile name is:{2}", c.Name, c.Age, _fileName));
}
}
public interface ICustomerRepository
{
Customer GetCustomer();
}
public class CustomerRepository : ICustomerRepository
{
public Customer GetCustomer()
{
return new Customer() { Name = "Ciaran", Age = 29 };
}
}
public class Customer
{
public string Name { get; set; }
public int Age { get; set; }
}
}