OptionsModel dependency injection in vNext console application - dependency-injection

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()

Related

Get Multiple Connection Strings in appsettings.json without EF

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

Self referencing loop detected when serializing Data models in MVC5 / EF6

I am getting this error "Self referencing loop detected" while serializing using 'Json.NET'
I have a Book model
public class Book
{
public Book()
{
BookPersonMap = new List<BookPersonMap>();
}
public int BookId { get; set; }
public virtual ICollection<BookPersonMap> BookPersonMap { get; private set; }
(And many other virtual Icollections)
}
And this is the BookPerson Mapping class:
public class BookPersonMap
{
public int BookId { get; set; }
public string PersonName { get; set; }
public int PersonTypeId { get; set; }
public virtual Book Book { get; set; } // Foreign keys
public virtual PersonType PersonType { get; set; }
}
When I try to Serialize the Book object it throws:
"Self referencing loop detected for property 'Book' with type 'System.Data.Entity.DynamicProxies.Book_57F0FA206568374DD5A4CFF53C3B41CFDDC52DBBBA18007A896 08A96E7A783F8'. Path 'BookPersonMap[0]'."
I have tried the things suggested in some of the similar posts
Example:
PreserveReferencesHandling = PreserveReferencesHandling.Objects in Serializer settings returned a string with length 3 million!
ReferenceLoopHandling = ReferenceLoopHandling.Ignore in Serializer settings :
"An exception of type 'System.OutOfMemoryException' occurred in Newtonsoft.Json.dll but was not handled in user code"
^ Same luck with "ReferenceLoopHandling.Serialize"
MaxDepth = 1 : Infinite loop again.
Putting [JsonIgnore] on the virtual properties is working but it is a tedious task (because of numerous FK references) and not efficent, since if I miss one property and it will throw exception.
What is missing from above Json settings for them be not working?
services.AddMvc().AddJsonOptions(opt => {
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
I have found the best way to solve this type of error is to flatten your model using a view model.
Put a break point on your object before it is serialized and start drilling into the child properties. You will probably find that you can go on indefinitely.
This is what the serializer is choking on.
Create a Constructor for your controller and put on it this line of code :
db.Configuration.ProxyCreationEnabled = false;
//db is the instance of the context.
For asp.net mvc 5 use this
Add the code below to your Application_Start method inside globax.asax file or startup file.
protected void Application_Start()
{
..
GlobalConfiguration.Configuration.Formatters.JsonFormatter
.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}
Disable lazy loading and
ensure your controller does not return
Json(..obj)
rather it should return
Ok(..obj)

Dynamically assigning a Domain Model within a Domain Model

This is my first question, and I've agonised over what to write for a couple of days while Ive been trying to solve this problem.
I bought the Dependency Injection in .NET book by Mark Seeman, and have been trying to follow that and the examples on the Ninject website for creating an abstract factory class. The general idea is that I have a form contains a list of answers to questions. Answers can be of various types, so I am using a factory to create the relevant answer type.
I'm getting the error:
Error activating IAnswerValue
No matching bindings are available, and the type is not self-bindable.
Activation path:
1) Request for IAnswerValue
Suggestions:
1) Ensure that you have defined a binding for IAnswerValue.
2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
3) Ensure you have not accidentally created more than one kernel.
4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
5) If you are using automatic module loading, ensure the search path and filters are correct.
I initially tried with a parameter, but to simplify things for this example, Ive stripped it all out. None of the suggestions given in the error message seem to apply, the factory type is binding, as is the form service but the answervalue is apparently not.
This is the code from my NinjectWebCommon.cs
kernel.Bind<DomainModel.IAnswerValue>().To<DomainModel.AnswerValue>();
kernel.Bind<DomainModel.IAnswerValue>().To<DomainModel.StringAnswerValue>();
kernel.Bind<DomainModel.IAnswerValue>().To<DomainModel.DateTimeAnswerValue>();
kernel.Bind<IAnswerValueFactory>().ToFactory();
This is the answer class definition:
public class Answer
{
readonly IAnswerValueFactory answerValueFactory;
public int Id { get; set; }
public Question Question { get; set; }
public string Type { get; set; }
public Answer(IAnswerValueFactory answerValueFactory)
{
this.answerValueFactory = answerValueFactory;
}
public void GetAnswerValue()
{
var answer = this.answerValueFactory.GetAnswerValue();
}
public List<AnswerItem> PotentialAnswers { get; set; }
}
and the answer value:
public interface IAnswerValue
{
AnswerValue GetAnswerValue();
}
public class AnswerValue : IAnswerValue
{
readonly IAnswerValue answerValue;
public AnswerValue() { }
public AnswerValue(IAnswerValue answerValue)
{
this.answerValue = answerValue;
}
public AnswerValue GetAnswerValue()
{
// this will contain a switch statement to
// determine the type returned but I have
// omitted for this example
return new StringAnswerValue();
}
}
public class StringAnswerValue : AnswerValue
{
public string StringAnswer { get; set; }
}
and the factory:
public class AnswerValueFactory : IAnswerValueFactory
{
readonly IAnswerValue answerValue;
public AnswerValueFactory(IAnswerValue answerValue)
{
this.answerValue = answerValue;
}
public IAnswerValue GetAnswerValue()
{
return (IAnswerValue)this.answerValue.GetAnswerValue();
}
}
I feel like Ive exhausted my knowledge and Im just going around in circles trying the same thing over and over. There must be something quite simple Im missing, but I just cant see what it is.

Ninject - how and when to inject

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; }
}
}

ASP.NET MVC - Ninject 2.0 Activation Error

I just started working with dependency injection for the first time and I am using as Ninject 2.0 as my IoC container in an ASP.NET MVC 2 website and I'm hitting an activation error that I am not sure how to react to. I am sure it's simple so hopefully someone can point me in the right direction without too much thought.
I have a property on my class BaseController which takes an IWebsiteSettings and is flagged with the [Inject] attribute. In my StandardKernel I load a module with the following code:
public class WebModule : Module
{
public override void Load()
{
Bind<IWebsiteSettings>()
.ToProvider(new WebsiteSettingsProvider(WebConfigurationManager.AppSettings))
.InSingletonScope();
}
}
public class WebsiteSettingsProvider : Provider<WebsiteSettings>
{
private const string WebsiteNameKey = "Website.Name";
private const string ContactFormEmailSubjectKey = "ContactForm.EmailSubject";
private const string ProductImageDirectoryKey = "Products.ImageDirectory";
private const string UploadTempDirectoryKey = "Uploads.TempDirectory";
protected NameValueCollection Settings { get; set; }
public WebsiteSettingsProvider(NameValueCollection settings)
{
Settings = settings;
}
protected override WebsiteSettings CreateInstance(IContext context)
{
return new WebsiteSettings
{
WebsiteName = Settings[WebsiteNameKey] ?? string.Empty,
ContactFormEmailSubject = Settings[ContactFormEmailSubjectKey] ?? string.Empty,
ProductImageDirectory = Settings[ProductImageDirectoryKey] ?? string.Empty,
UploadsTemporaryDirectory = Settings[UploadTempDirectoryKey] ?? string.Empty
};
}
}
This is fairly straightforward- I'm trying to load some data from the web.config file and store it in a singleton object for use across my controllers. The call to Bind seems to function exactly as it should and the Settings property in my provider is correctly initialized with the AppSettings collection in the config file. Still, when the application loads the first time:
Server Error in '/' Application.
Error activating SByte* using implicit self-binding of SByte*
No constructor was available to create an instance of the implementation type.
Activation path:
4) Injection of dependency SByte* into parameter value of constructor of type string
3) Injection of dependency string into property WebsiteName of type WebsiteSettings
2) Injection of dependency IWebsiteSettings into property WebsiteSettings of type HomeController
1) Request for HomeController
Suggestions:
1) Ensure that the implementation type has a public constructor.
2) If you have implemented the Singleton pattern, use a binding with InSingletonScope() instead.
Interestingly, if I refresh the page I don't get the exception and a call to Kernel.Get() returns the correct object.
Any advice?
(We talked about this on IRC, but I'm putting it here in case someone else runs into this problem as well.)
WebsiteSettings has [Inject] attributes on its properties, so Ninject is trying to resolve a binding from System.String to inject a value into the properties. Since you're using a custom provider to activate WebsiteSettings instances, you don't need [Inject] attributes on its properties.
The offending code was actually in the class WebsiteSettings where I was doing this:
public class WebsiteSettings : IWebsiteSettings
{
[Ninject.Inject]
public string WebsiteName
{
get; set;
}
[Ninject.Inject]
public string UploadsTemporaryDirectory
{
get; set;
}
[Ninject.Inject]
public string ContactFormEmailSubject
{
get; set;
}
[Ninject.Inject]
public string ProductImageDirectory
{
get; set;
}
}
By placing the Inject attribute on my properties I was causing Ninject to try to assign values that I never bound. Because I am using a Provider to load my type I do not need to include the Inject attribute.

Resources