I have some problem with understanding how to create injectable classes…
Here is my example:
public interface IService
{
string FindSomeData()
}
Now we create a class which implements the interface:
public class FolderService : IService
{
private string _path;
public FolderService(string path)
{
_path = path;
}
public string FindSomeData()
{
//Open a folder using _path and find some data
}
}
And maybe other class:
public class DbService : IService
{
private MyConnectionClass _connection;
public DbService(MyConnectionClass connection)
{
_connection = connection;
}
public string FindSomeData()
{
//Connect to database using _connection object and find some data
}
}
Now I would like to add one of the classes to IoC Container e.x.:
if (InDebug)
SimpleIoc.Default.Register<IService, FolderService>();
else
SimpleIoc.Default.Register<IService, DbService>();
And know I have a problems.
When I want to pass this object to the constructor of some other classes:
public MyViewModel(IService service)
{
_service = service;
}
// Read folder name from TextBox on View and then call _service.FindSomeData
Then I would like to pass user selected path to the IService object (FolderService) in this case.
How should I do this in a correct way (according to SOLID and other good practiciess patterns…)?
Once I should pass string (folder path), once a MyConnectionClass (if connection to database).
What is the best way to do that kind of things?
Best regards,
Michal
You can encapsulate folder path provide/change logic into a separate provider like IFolderPathProvider and inject it into FolderService
public interface IFolderPathProvider {
string GetFolderPath();
void SetFolderPath(string);
}
public class FolderPathProvider : IFolderPathProvider {
...
}
public class FolderService : IService
{
private IFolderPathProvider _folderPathProvider;
public FolderService(IFolderPathProvider folderPathProvider)
{
_folderPathProvider = folderPathProvider;
}
public string FindSomeData()
{
string path = _folderPathProvider.GetFolderPath();
//Open a folder using path and find some data
}
}
When user changes the path, inject IFolderPathProvider to the handler and call SetFolderPath. Similarly, you can create IDbConnectionProvider. Depending on the situation, they can be combined into one DataConfigProvider but I 'm not sure what exactly do you need there; the main idea is to separate folderpath/dbconnection changing logic from the services and keep using dependency injection.
Related
I have been following this tutorial in order to get access to my appsettings.json from my MVC project inside my class library.
geek-tutorial
I have a class as such in my class library
using dapper;
public class SqlDataAccess : IConfigManager
{
private readonly IConfiguration _configuration;
public SqlDataAccess(IConfiguration configuration)
{
this._configuration = configuration;
}
public List<T> LoadData<T>(string sql)
{
using (IDbConnection cnn = new SqlConnection(GetConnectionString()))
{
return cnn.Query<T>(sql).ToList();
}
}
public int SaveData<T>(string sql, T data)
{
using (IDbConnection cnn = new SqlConnection(GetConnectionString()))
{
return cnn.Execute(sql, data);
}
}
public string GetConnectionString(string connectionName = "URLShortnerDB")
{
return this._configuration.GetConnectionString(connectionName);
}
}
Interface:
public interface IConfigManager
{
string GetConnectionString(string connectionName);
}
I have added services.AddSingleton<IConfigManager, SqlDataAccess>(); in my mvc startup.cs
However now I would like to use my SqlDataAccess class and call methods from another class e.g:
public static class ShortUrlProcessor
{
public static ShortURLModel GetOriginalURL(string shortUrl)
{
string sql = $#"SELECT * FROM dbo.shorturl WHERE shortUrl = '{ shortUrl }'";
var originalURLEnum = SqlDataAccess.LoadData<ShortURLModel>(sql); //<--- problem
return originalURLEnum.First();
}
}
However SqlDataAccess is not instantiated, and in order to do var _sqldataaccess = SqlDataAccess() I need to pass in a parameter as defined in the constructor of the class. I do not know what to pass in? I do not have any IconfigurationManager in this ShortUrlProcessor class. I understand the reason of doing this is dependancy injection, however I am still not grasping how this all works?
You're very close, but you need to fix a few things. SqlDataAccess implements IConfigManager. Why? What's that providing? Instead, you should have it implement an interface that allows it to expose the functionality other classes depend on.
public interface ISqlDataAccess
{
List<T> LoadData<T>(string sql);
int SaveData<T>(string sql, T data);
}
Change your SqlDataAccess class to implement this interface...
public class SqlDataAccess : ISqlDataAccess
And of course, wire this up with your DI container.
services.AddTransient<ISqlDataAccess, SqlDataAccess>();
Now, any class that needs to run SQL can take a dependency on the ISqlDataAccess interface, utilizing constructor injection to get an instance of ISqlDataAccess. Since we've told the DI container to provide a SqlDataAccess instance when the ISqlDataAccess dependency is present, it will all wire up nicely in your app.
Then we have the issue with ShortUrlProcessor. You declared that class as static. That's bad, because it makes it difficult for it to use constructor injection to get its dependencies, and any other class that needs to invoke its methods has to do so directly, rather than via an abstraction. That violates the Dependency Inversion Principle of SOLID. And since we should always strive to write SOLID code because of the maintainability and testability, we need to fix that.
public class ShortUrlProcessor : IShortUrlProcessor
{
readonly ISqlDataAccess _dataAccess;
public ShortUrlProcessor(ISqlDataAccess dataAccess)
{
_dataAccess = dataAccess;
}
public ShortURLModel GetOriginalURL(string shortUrl)
{
string sql = $#"SELECT * FROM dbo.shorturl WHERE shortUrl = '{ shortUrl }'";
var originalURLEnum = _dataAccess.LoadData<ShortURLModel>(sql); //<--- problem
return originalURLEnum.First();
}
}
And we'll need an interface so other classes don't have to depend directly on ShortUrlProcessor...
public interface IShortUrlProcessor
{
ShortURLModel GetOriginalURL(string shortUrl);
}
And of course, we need to register it with our DI container.
services.AddTransient<IShortUrlProcessor, ShortUrlProcessor>();
Then any class that needs to access the functionality of ShortUrlProcessor can do so via the abstraction IShortUrlProcessor. You mentioned you have a controller calling this, so let's wire that up too.
public class MyController()
{
readonly IShortUrlProcessor _shortUrlProcessor;
public MyController(IShortUrlProcessor shortUrlProcessor)
{
_shortUrlProcessor = shortUrlProcessor;
}
public ActionResult SomeActionMethod()
{
var model = _shortUrlProcessor.GetOriginalURL("asdf");
return View(model);
}
}
We don't have to create an interface for the controller, because the controller will be called by the framework. And we don't have to wire up the controller with the DI container, because the framework handles that for us.
By doing all this, we can easily test individual methods in isolation. There's still some improvements to be made (the SQL Injection attack I mentioned in the comments needs to be fixed), but it's a good step in the right direction.
I'm using Simple Injector as DI Container in a project.
The problem is that I have a SqliteStorage-class, which needs the path to the db. There are multiple dbs, so I need a way to inject the path to the SqliteStorage-class at creation.
My code looks as follows (simplified without interfaces):
public class SqliteStorageOptions
{
public string Path {get; set;}
}
public class SqliteStorage
{
private readonly string _path;
public SqliteStorage(SqliteStorageOptions options)
{
_path = options.Path;
}
}
public class Db1
{
private readonly SqliteStorage _sqlite;
public Db1(SqliteStorage sqlite)
{
_sqlite = sqlite;
}
}
public class Db2
{
private readonly SqliteStorage _sqlite;
public Db1(SqliteStorage sqlite)
{
_sqlite = sqlite;
}
}
// without di
var db1 = new Db1(new SqliteStorage(new SqliteStorageOptions { Path = "db1.db" });
var db2 = new Db2(new SqliteStorage(new SqliteStorageOptions { Path = "db2.db" });
Possible Solutions:
Include SqliteStorageOptions as parameter at every method in SqliteStorage.
Provide a init-method in SqliteStorage
Create a SqliteStorageFactory with a public SqliteStorage Create(SqliteStorageOptions options)-method.
So are there any built-in solution to my problem in simple-injector or can someone provide another (better) solution?
Thanks
Edit 1:
I added some code. Db1 and Db2 both connect to sqlite-dbs (different dbs, different schema), so I wanted to extract all the sqlite-stuff to its own class SqliteStorage. So, the SqliteStorage needs to know the db path.
Which solution is best depends a bit on whether you require Auto-Wiring (automatic constructor injection) or not. Using conditional registrations (using RegisterConditional) is a good pick, but you have be aware that it is limited to determining the injection based on only its direct parent. This means that you can't make SqliteStorageOptions conditional based on its parent parent (either Db1 or Db2).
If the Db1 and Db2 classes solely depend on a SqliteStorage and don't require any other dependencies, Auto-Wiring is not a real issue and your registrations can be as simple as the following:
container.Register<Db1>(
() => new Db1(new SqliteStorage(new SqliteStorageOptions { Path = "db1.db" }));
container.Register<Db2>(
() => new Db2(new SqliteStorage(new SqliteStorageOptions { Path = "db2.db" });
In case Auto-Wiring is required inside Db1 and Db2, RegisterConditional gives a good alternative, because it enables Auto-Wiring:
container.Register<Db1>();
container.Register<Db2>();
container.RegisterConditional<SqliteStorage>(
Lifestyle.CreateRegistration(
() => new SqliteStorage(new SqliteStorageOptions { Path = "db1.db" }),
container),
c => c.Consumer.ImplementationType == typeof(Db1));
container.RegisterConditional<SqliteStorage>(
Lifestyle.CreateRegistration(
() => new SqliteStorage(new SqliteStorageOptions { Path = "db2.db" }),
container),
c => c.Consumer.ImplementationType == typeof(Db2));
In this code snippet, both Db1 and Db2 are registered 'normally', while the SqliteStorage registrations are conditionally injected based on thei consumer.
This registration is more complex, because RegisterConditonal need to be supplied with a Registration instance: there is no RegisterConditional overload that directly accepts a Func<T> factory delegate.
You can have 2 singletons one per each database connection. Let's consider an example, firstly we'll need to create an interface for your StorageService:
public interface IStorage
{
void UsePath();
}
Now let's create couple of implementations of this storage service:
public class RedisStorage: IStorage
{
private readonly string _path;
public RedisStorage(string path)
{
_path = path;
}
public void UsePath()
{
Console.WriteLine($"Here's path: {_path}");
}
}
public class SqlStorage: IStorage
{
private readonly string _path;
public SqlStorage(string path)
{
_path = path;
}
public void UsePath()
{
Console.WriteLine($"Here's path: {_path}");
}
}
Enum to differentiate between implementations of IStorage:
public class StorageSource
{
public enum StorageTypes
{
Redis=1,
Sql=2
}
}
Once we are done with that, let's create a wrapper for a storage source:
public interface IStorageWrapper
{
void DoStuff();
}
Now comes a tricky part, instantiate a storage wrapper service decorator:
public class StorageServiceWrapper: IStorageWrapper
{
private readonly Func<string, IStorage> _storage;
public StorageServiceWrapper(Func<string, IStorage> storage)
{
_storage = storage;
}
public void UsePath()
{
_storage(StorageSource.StorageTypes.Redis.ToString()).DoStuff();
//uncomment for sql
//_storage(StorageSource.StorageTypes.Sql.ToString()).DoStuff();
}
}
To achieve this, you will need to register your classes in Startup.cs as follows:
services.AddScoped<IStorageWrapper, StorageServiceWrapper>();
services.AddSingleton<RedisStorage>();
services.AddSingleton<SqlStorage>();
services.AddTransient<Func<string, IStorage>>(serviceProvider => key =>
{
switch (key)
{
case "Redis":
return serviceProvider.GetService<RedisStorage>();
default:
return serviceProvider.GetService<SqlStorage>();
}
});
This wouldn't be as beautiful as calling _storage.DoStuff();, but I believe would help you with the solution of your problem. If you still want to keep it handy, consider managing your settings file and injecting proper IOptions<> instance with a conn string you need and registering a factory method.
I am trying to develop a DisplayName Attribute which has an interface for localization service, which is already registered at startup and working if injected in a constructor.
How can I get the localization service interface to be instantiated since I cant use a construction injection?
This is my code
public class MyDisplayNameAttribute : System.ComponentModel.DisplayNameAttribute
{
private string _resourceValue = string.Empty;
private ILocalizationService _localizationService;
public MyDisplayNameAttribute(string resourceKey)
: base(resourceKey)
{
ResourceKey = resourceKey;
}
public string ResourceKey { get; set; }
public override string DisplayName
{
get
{
_resourceValue = _localizationService.GetLocaleString(ResourceKey);
return _resourceValue;
}
}
public string Name
{
get { return nameof(MyDisplayNameAttribute); }
}
}
Thanks
I hope you could solve the problem, this is a very simple solution but as you knew it's an anti-pattern :
public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
public LocalizedDisplayNameAttribute(string Name) : base(Name)
{
}
public override string DisplayName
{
get
{
var _localizationService= new HttpContextAccessor().HttpContext.RequestServices.GetService<ILocalizationService>();
return _localizationService.Get(base.DisplayNameValue).Result;
}
}
}
I Hope it helps others at least ;)
Dependency injection is working with invertion of control (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2). So framework controls your app and when instantiating your clsses injects dependencies requested via constructor.
Refer also here How to create a class without constructor parameter which has dependency injection
So I suspect that it is not possible to inject dependency without using constructor.
May be if you describe you intention there may be another good solution.
My controller which returns user details by calling an API intern
public class HomeController : Controller
{
public ActionResult AccountDetails(int userId)
{
return this.Content(new WebHelperService().GetAccountDetails(userId)), "application/json");
}
}
Here is my WebHelperService which is in Business Layer, where i need to get value from appsettings.json
public class WebHelperService
{
private string url = null;
public WebHelperService()
{
//url = ConfigurationManager.ConnectionString["ExternalApiUrl"].ToString();
// ConfigurationManager is not available in .net core.
//So How do i read ExternalApiUrl from appsettings.josn,Which is the best way
}
public string GetAccountDetails(int userId)
{
return WebCall("{'userId':" + userId + "}");
}
private string WebCall(string data)
{
WebRequest request = WebRequest.Create(url);
// get the data from url and returns it
}
}
Do I need to carry settings all the way from controller in mvc6?
Reference : learn.microsoft.com/en-us/aspnet/core/mvc/con..)
Let's forget for a moment your particular use case and just talk about settings in .net core in general. Importantly, I think you are trying to access the raw AppSettings from your class, but what you actually want to do is DI them into your class. So let's do that.
Consider you have a appSettings.json that resembles something like below :
{
"myConfiguration": {
"myProperty": true
}
}
Now you need to create a POCO to hold these settings. Something like so :
public class MyConfiguration
{
public bool MyProperty { get; set; }
}
In your startup.cs you should have a method called "ConfigureServices". In there you are going to place a call to "configure" your settings like so.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyConfiguration>(Configuration.GetSection("myConfiguration"));
}
And so now you want to inject that settings object into a class. Let's call it MyClass for now. It would look like the following :
public class MyClass : IMyClass
{
private readonly MyConfiguration _myConfiguration;
public MyClass(IOptions<MyConfiguration> myConfiguration)
{
_myConfiguration = myConfiguration.Value;
}
}
Now you have access to your configuration!
Bonus!
Instead you can make your ConfigureServices method look like the following :
public void ConfigureServices(IServiceCollection services)
{
//services.Configure<MyConfiguration>(Configuration.GetSection("myConfiguration"));
services.AddSingleton(Configuration.GetSection("myConfiguration").Get<MyConfiguration>());
}
What this now does is bind your services onto an actual class, not the IOptions object.
Now when you inject it into your class, you instead inject the POCO settings class, not IOptions. Like so :
public class MyClass : IMyClass
{
private readonly MyConfiguration _myConfiguration;
public MyClass(MyConfiguration myConfiguration)
{
_myConfiguration = myConfiguration;
}
}
For further reading :
http://dotnetcoretutorials.com/2016/12/26/custom-configuration-sections-asp-net-core/
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration
I have an ASP.NET 5 MVC 6 application. It has a Data Access library which needs a connection string to make a connection to the database.
Currently I am passing a strongly typed configuration settings class with connection string as a public property all the way up from the MVC controllers (Where it is received through DI) to the Data Access Class library.
I want to know if there is a better way for a class library to access strongly typed configuration settings using dependency injection or any other mechanism ?
Thank you.
EDIT : Code Example
This is a generic DbTransaction class which is called from the business layer.
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(string connectionString)
{
_query = new Query(connectionString);
}
public TEntity GetById(long id)
{
var sqlGenerator = new SqlGenerator<TEntity>();
var sql = sqlGenerator.GetSelectByIdQuery();
var mapper = new NMapper.Mapper<TEntity>();
var cmd = _query.GetNpgsqlCommand(sql, new { id });
return mapper.GetObject(cmd);
}
}
The query class creates the connection object from the connection string that is provided to it.
I agree with #Steven that using IOptions<T> is a bad idea. You can however use the ConfigurationBinder extensions to read out a specific section of configuration into a strongly-typed POCO class. Just make sure you have this somewhere in your project.json's dependencies section:
"dependencies": {
[other dependencies],
"Microsoft.Extensions.Configuration.Binder": "1.0.0-rc1-final",
[other dependencies]
}
Just build up your configuration as normal. For example, say you had a Database.json configuration file that looked like this:
{
"Database": {
"ConnectionInfo": {
"connectionString": "myConnectionString"
}
}
}
You can build your configuration from the Startup method in Startup.cs:
public IConfiguration Configuration { get; private set; }
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) {
IConfigurationBuilder configBuilder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("Database.json")
.AddEnvironmentVariables()
Configuration = configBuilder.Build();
}
Now we can make a POCO class to match the "Database:ConnectionInfo" section of the JSON configuraiton file. You can match it to an interface as #janhartmann suggests, but it may or may not be necessary.
public class DatabaseConnectionInfo {
public string ConnectionString { get; set; }
}
Now, how can we get that DatabaseConnectionInfo class populated with the data from the JSON config file? One way is to use the IOptions<T> framework type, but I don't like using framework types when I can avoid them. Instead, you can get an instance like so:
DatabaseConnectionInfo dbConnInfo = Configuration
.GetSection("Database:ConnectionInfo")
.Get<DatabaseConnectionInfo>();
Now you can just register the dbConnInfo type as a singleton of the type DatabaseConnectionInfo (or as a singleton of an interface type if you prefer to have an immutable configuration settings object). Once it's registered in the IoC container, you can constructor inject it where needed:
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(DatabaseConnectionInfo dbConnInfo)
{
_query = new Query(dbConnInfo.ConnectionString);
}
public TEntity GetById(long id) { ... }
}
You can let your service class depend on a an interface, e.g.:
public interface IConnectionFactory {
string ConnectionString();
}
public class MyDataAccessClass {
private readonly IConnectionFactory _connectionFactory
public MyDataAccessClass(IConnectionFactory connectionFactory) {
_connectionFactory = connectionFactory;
}
public void Whatever() {
var connectionString = _connectionFactory.ConnectionString();
}
}
And then make an implementation of it (as near to your composition root as possible):
public class SqlConnectionFactory : IConnectionFactory {
public string ConnectionString() {
return "myConnectionString";
}
}
Let the interface have the methods or properties you need.
Wire like:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConnectionFactory, SqlConnectionFactory>();
}
I use a similar method to some of those listed earlier, but I think its sufficiently different to warrant another answer.
Firstly I define an interface with all the configuration that my class needs. In this case
public interface IDbTransactionsConfiguration {
string ConnectionString { get; }
}
Then I alter my class to take this configuration via constructor injection
public class DbTransactions<TEntity> where TEntity : DbEntity, new() {
public DbTransactions(IDbTransactionsConfiguration configuration) {
...
}
}
Then I define a class that handles all the configuration for my application.
public class MyApplicationConfiguration : IDbTransactionsConfiguration, ISomeOtherConfiguration, etc {
public string ConnectionString { get; }
... other configuration
}
Then I pass this class into all classes that need it using some kind of Depenendency Injection (normally Castle Windsor or AutoFac for me).
If it is too difficult to construct DbTransactions for legacy type reasons, I define a static version of MyApplicationConfiguration and access this directly.
More details on this blog post.