Is it bad style to inject IConfigurationRoot into a class of the business logic- or data access-layer? - asp.net-core

We have an ASP.NET Core 1.1 application which is splitted in 3 layers:
Web
Business Logic (BLL)
Data Access (DAL)
There we have some methods of the BLL and some of the DAL that need some values from the config. I see two possibilities to pass them the required values:
define the values as parameters of the method, get them in the web-layer and pass them to the BLL and DAL
inject IConfigurationRoot to the BLL and DAL
Which of the both possibilities is more recommendable?

With ASP.NET Core you should actually choose the third possibility:
use strongly typed settings injected with IOptions<T> wrapper.
Here is a sample:
POCO for the settings:
public class SomeSettings
{
public string SomeStringValue { get; set; }
public int SomeNumericValue { get; set; }
// ...
}
Injecting the settings:
public class SomeClass
{
private readonly SomeSettings settings;
public SomeClass(IOptions<SomeSettings> options)
{
this.settings = options.Value;
}
}
Registering the settings:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.Configure<SomeSettings>(Configuration.GetSection("SectionNameHere"));
}
Using Options pattern is recommended way to deal with configuration in .NET Core.

Related

How to write an extension method that allows you to set options without creating the options instance

I really like the pattern where I can configure a service through an option class without having to create it, but I can't find an example of how to write an extension method that allows me to use that same pattern such as the one below that exists for registering a DbContext.
services.AddDbContext<MyDbContext>(options => options.EnableDetailedErrors());
I can see the method signature uses an action method, but I can't seem to find the extension class in GitHub for ASP.NET Core that shows me how to write an extension method using that type of option builder pattern.
For example, take the following service code. How would I write the extension method so that I could configure the options during service registration.
public void ConfigureServices(IServiceCollection services)
{
services.AddMyService(options => options.SomeSetting = true);
}
public interface IMyService
{
void DoSomething();
}
public class MyService : IMyService
{
private readonly MyServiceOptions _options;
public MyService(IOptions<MyServiceOptions> options)
{
_options = options.Value;
}
public void DoSomething()
{
Console.WriteLine(_options.SomeSetting);
}
}
public static class MyServiceExtensions
{
// How would I write this extension method so that I could configure it with options overload
public static IServiceCollection AddMyService(this IServiceCollection services, Action<MyServiceOptions> configure)
{
services.AddSingleton<IMyService, MyService>();
return services;
}
}
ASP.NET Core provides this mechanism with the IConfigureOptions
interface. You implement this interface in a configuration class and
use it to configure the IOptions object in any way you need.
It's as easy as:
public class MyServiceConfiguration : IConfigureOptions<MyServiceOptions>
{
private MyServiceOptions _options;
public MyServiceConfiguration(IOptions<MyServiceOptions> options)
{
_options = options.Value;
}
public void Configure(MyServiceOptions options)
{
options.SomeSetting = _options.SomeSetting;
options.SomeOtherSetting = _options.SomeOtherSetting;
}
}
All that remains is to register this implementation in the DI container.:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyServiceOptions>(options => options.SomeOtherSetting = true);
services.AddSingleton<IMyService, MyService>();
}
With this configuration, when IOptions is injected into your service, the MyServiceOptions object will be configured by the ConfigureMyServiceOptions class.
Be careful! The ConfigureMyServiceOptions object is registered as a singleton,
so it will capture any injected services of scoped or transient lifetimes.

Creating a database context using the database first approach with entityframework core.

I want to be able to create a database context with entityframework core in my webapi project using the database first approach.
When I create like this it works very well
public class TestingContext : DbContext
{
public TestingContext(DbContextOptions<TestingContext> options)
: base(options)
{
}
public TestingContext()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=xxxxxx;Initial Catalog=xxxxxx;Integrated Security=False;User Id=xxxxx;Password=xxxxx;MultipleActiveResultSets=True");
}
public DbSet<Information> Information { get; set; }
public DbSet<ArticleUser> ArticleUser { get; set; }
}
I had to add the line services.AddDbContext to make it work.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddCors();
//using Dependency Injection
services.AddSingleton<Ixxx, xxx>();
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddDbContext<TestingContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "Articles API", Version = "v1" });
});
}
If I remove this method from my TestingContext
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=xxxxxx;Initial Catalog=xxxxxx;Integrated Security=False;User Id=xxxxx;Password=xxxxx;MultipleActiveResultSets=True");
}
I get the error below.
No database provider has been configured for this DbContext.
A provider can be configured by overriding the DbContext.OnConfiguring method or
by using AddDbContext on the application service provider. If AddDbContext is used,
then also ensure that your DbContext type accepts a DbContextOptions object in its
constructor and passes it to the base constructor for DbContext.
Why do I need to pass my connection string to the database in two places before it can pull my data. Please assist. I am new to the core. The two places are configure services method and the context itself.
Option 1: Remove parameterized constructor and OnConfiguring. Result:
public class TestingContext : DbContext
{
public DbSet<Information> Information { get; set; }
public DbSet<ArticleUser> ArticleUser { get; set; }
}
Option 2: Remove parameterized constructor and options in ConfigureServices in AddDbContext
Result:
In Startup.cs
services.AddDbContext<TestingContext>();
In TestingDbContext.cs
public class TestingDdContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=xxxxxx;Initial Catalog=xxxxxx;Integrated Security=False;User Id=xxxxx;Password=xxxxx;MultipleActiveResultSets=True");
}
public DbSet<Information> Information { get; set; }
public DbSet<ArticleUser> ArticleUser { get; set; }
}
Option 3: A parametric constructor is needed to create factory. Example:
public class TestDdContext : DbContext
{
public TestDdContext(DbContextOptions options) : base(options)
{
}
//TODO: DbSets
}
public class TestDbContextFactory : IDbContextFactory<TestDdContext>
{
public TestDdContext Create(DbContextFactoryOptions options)
{
var contextOptions = new DbContextOptionsBuilder();
contextOptions.UseSqlServer("...");
return new TestDdContext(contextOptions.Options);
}
}
If you are creating tests, do you need a backing Sql database? Would the In-memory provider not serve you better?
options.UseInMemoryDatabase("database-name");
For this reason, I'd ditch using the OnConfiguring method, and rely on passing the DbContextOptions to your constructor
Side note, you have to consider what you are testing - are you testing your code that is dependent on your DbContext, or are you testing your DbContext itself - if there is no custom logic and you are merely extending the DbContext, there may not be enough value in writing tests for it - and you're not responsible for testing EFCore itself.

How to pass IOptions through Dependency injection to another Project

I have a WebApplication targetting .net core.
I have also created a Class Library targetting .net core as well.
I am creating a Users Repository following this Dapper tutorial Here
It would be nice to be able to provide the option that was injected in start up of the WebApplication into the project that will be the data access layer.
Here is the code for the Users Repository in a separate project.
class UsersRepository
{
private readonly MyOptions _options;
private string connectionString;
public UsersRepository(IOptions iopt/// insert Option here )
{
_options = iopt.Value;
connectionString = _options.connString;
}
public IDbConnection Connection
{
get
{
return new SqlConnection(connectionString);
}
}
The WebApplication Project Startup looks as follows.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<MyOptions>(Configuration);
services.AddMvc();
}
and of course MyOptions is a class in the web application that has only one property connString
One possible design is to make a new interface for your repository configuration inside your class library, and have your MyOptions type implement that interface.
For example, in your class library you can do the following:
public interface IRepositoryConfig
{
string ConnectionString { get; }
}
public class UserRepository
{
public UserRepository(IRepositoryConfig config)
{
// setup
}
}
And in your WebAPI Startup class you can wire this up as follows:
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<MyOptions>(Configuration);
services.AddMvc();
services.AddScoped<IRepositoryConfig>(s =>
s.GetService<IOptions<MyOptions>>().Value
);
services.AddScoped<UserRepository>();
}
Doing this will allow you to use the Asp.Net Core configuration/options framework without having to reference any Asp.Net DLLs in your class library directly.

ASP.NET 5 Non-Controller DI injection

I am trying to use the new ASP.NET 5 dependency injection system, but it seems limited to ONLY constructors of classes that inherit from Controller.
Is there any other way to inject things? Properties? Anything? This is so severely limiting and has had me brickwalling for days.
Just tested this (RC1 Update1), it works with other classes as well.
I wrote a small example, first the type declarations:
public interface IBaseServiceType { }
public interface IComposedServiceType
{
IBaseServiceType baseService { get; }
}
public class BaseServiceImplementation : IBaseServiceType { }
public class ComposedServiceImplementation : IComposedServiceType
{
public IBaseServiceType baseService { private set; get; }
public ComposedServiceImplementation(IBaseServiceType baseService)
{
this.baseService = baseService;
}
}
The configuration:
services.AddTransient(typeof(IBaseServiceType), typeof(BaseServiceImplementation));
services.AddTransient(typeof(IComposedServiceType), typeof(ComposedServiceImplementation));
And create the instance like this where context is your HttpContext:
var composedServiceInstance = context.ApplicationServices.GetService<IComposedServiceType>();
Register your class as a service and treat it like you would all other services
see
Net Core Dependency Injection for Non-Controller

Dapper and DAL Where must i place my validation

I am started with my first Dapper Dal project.
I have three projects:
- Website (MVC)
- DataLayer (Dapper)
- Model (Poco Classes)
I want to add validation to my model but i also want to use clean poco classes for my datalayer. My datalayer use dapper to map my poco classes to the database.
I have searched the internet but i can't find a good answer.
My question is:
Where do i add my validation?
- In a seppetated project with classes that extend my poco classes or is there a different way?
If you want a clean separation between your DAL classes and your MVC classes, then you can do just that by, for instance, using ViewModels in your MVC-project. The ViewModel would have the properties and validations that works best with what you are presenting in the browser. Your controller would be responsible for mapping the data between the DAL classes and the ViewModels. Automapper is a very good tool for just that.
It would look a bit like the following:
DAL:
public class MyDapperClass
{
public int Id { get; set; }
public string SomeProperty { get; set; }
}
ViewModel:
public class MyViewModelClass
{
public int Id { get; set; }
[StringLength(50),Required]
public string SomeProperty { get; set; }
}
Controller:
// using AutoMapper;
public class MyController : Controller
{
public MyController()
{
// Set up AutoMapper to be able to map your class
Mapper.CreateMap<MyDapperClass, MyViewModelClass>();
}
public ActionResult MyAction()
{
var dalObject = DAL.GetObject();
var viewModel = Mapper.Map<MyViewModelClass>(dalObject);
return View(viewModel);
}
}