IOptionsSnapshot in .net core Console application not working - dependency-injection

Below is the example of .NET Core console application, uses constructor dependency injection with IOptionsSnapshot<T>.
class Program
{
static void Main(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json",
optional: false, reloadOnChange: true)
.Build();
IServiceProvider provider = new ServiceCollection()
.Configure<AppSettings>(
configuration.GetSection("AppSettings"))
.AddTransient<CheckOption>()
.BuildServiceProvider();
while (true)
{
if (Console.ReadLine() != "stop")
{
CheckOption checkOption =
provider.GetRequiredService<CheckOption>();
checkOption.Run();
}
else
{
return;
}
}
}
}
public class CheckOption
{
public AppSettings _AppSettings;
public CheckOption(IOptionsSnapshot<AppSettings> appSettings)
{
_AppSettings = appSettings.Value;
}
public void Run()
{
string option1 = _AppSettings.Option1;
string option2 = _AppSettings.Option2;
}
}
public class AppSettings
{
public string Option1 {get; set;}
public string Option2 {get; set;}
}
Values of Option1 and Option2 remains same, on change of "appsettings.json" in while loop.

Related

ASP.NET MVC 5 SignalR, SqlDependency and EntityFramework 6 - the sqlparameter is already contained by another sqlparametercollection

I am beginner with SignalR and SQLDepedency. I am trying to implement SignalR using EF Code First Approach. I am getting the error The sqlparameter is already contained by another sqlparametercollection if I am using where condition in LINQ class.
public class MessageHub : Hub
{
internal NotifierEntity NotifierEntity { get; private set; }
private MyDbContext db = new MyDbContext();
public void DispatchToClient()
{
Clients.All.broadcastMessage("Refresh");
}
public void Initialize(String userName)
{
if (!string.IsNullOrEmpty(userName))
{
NotifierEntity = db.GetNotifierEntity<Messages>(db.Messages.Where(x=>x.ApplicationUser.UserName== userName && !x.Status));
if (NotifierEntity == null)
return;
Action<String> dispatcher = (t) => { DispatchToClient(); };
PushSqlDependency.Instance(NotifierEntity, dispatcher);
}
}
}
The NotifierEntity Class
public class NotifierEntity
{
ICollection<SqlParameter> sqlParameters = new List<SqlParameter>();
public String SqlQuery { get; set; }
public String SqlConnectionString { get; set; }
public ICollection<SqlParameter> SqlParameters
{
get
{
return sqlParameters;
}
set
{
sqlParameters = value;
}
}
public static NotifierEntity FromJson(String value)
{
if (String.IsNullOrEmpty(value))
throw new ArgumentNullException("NotifierEntity Value can not be null!");
return new JavaScriptSerializer().Deserialize<NotifierEntity>(value);
}
}
public static class NotifierEntityExtentions
{
public static String ToJson(this NotifierEntity entity)
{
if (entity == null)
throw new ArgumentNullException("NotifierEntity can not be null!");
return new JavaScriptSerializer().Serialize(entity);
}
}
public class PushSqlDependency
{
static PushSqlDependency instance = null;
readonly SqlDependencyRegister sqlDependencyNotifier;
readonly Action<String> dispatcher;
public static PushSqlDependency Instance(NotifierEntity notifierEntity, Action<String> dispatcher)
{
if (instance == null)
instance = new PushSqlDependency(notifierEntity, dispatcher);
return instance;
}
private PushSqlDependency(NotifierEntity notifierEntity, Action<String> dispatcher)
{
this.dispatcher = dispatcher;
sqlDependencyNotifier = new SqlDependencyRegister(notifierEntity);
sqlDependencyNotifier.SqlNotification += OnSqlNotification;
}
internal void OnSqlNotification(object sender, SqlNotificationEventArgs e)
{
dispatcher("Refresh123");
}
}
public class SqlDependencyRegister
{
public event SqlNotificationEventHandler SqlNotification;
readonly NotifierEntity notificationEntity;
internal SqlDependencyRegister(NotifierEntity notificationEntity)
{
this.notificationEntity = notificationEntity;
RegisterForNotifications();
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security",
"CA2100:Review SQL queries for security vulnerabilities")]
void RegisterForNotifications()
{
using (var sqlConnection = new SqlConnection(notificationEntity.SqlConnectionString))
using (var sqlCommand = new SqlCommand(notificationEntity.SqlQuery, sqlConnection))
{
foreach (var sqlParameter in notificationEntity.SqlParameters)
{
sqlCommand.Parameters.Add(sqlParameter);
}
sqlCommand.Notification = null;
var sqlDependency = new SqlDependency(sqlCommand);
sqlDependency.OnChange += OnSqlDependencyChange;
if (sqlConnection.State == ConnectionState.Closed)
sqlConnection.Open();
sqlCommand.ExecuteNonQuery();
}
}
void OnSqlDependencyChange(object sender, SqlNotificationEventArgs e)
{
if (SqlNotification != null)
SqlNotification(sender, e);
RegisterForNotifications();
}
}
public delegate void SqlNotificationEventHandler(object sender, SqlNotificationEventArgs e);
If I am using the same query without any parameters, the code is working perfectly. I can see the database changes instantly in frontend. The issue is coming after added a parameter in Where clause.
I got this idea from below link
https://www.codeproject.com/Tips/1075852/ASP-NET-MVC-SignalR-SqlDependency-and-EntityFramew
Sourcecode link
we.tl/njwwLl8g36

Inject into the Startup class

Is it possible to use an IOC framework like Castle Windsor to inject into the Startup method. I mean something like this:
public class Startup()
{
IMyObject MyObject = new MyObject();
public Startup(MyObject myObject)
{
MyObject = myObject();
}
}
I am trying to drop and create a database on startup using NHibernate. Alternatively is there a "better" place to drop and create the database using NHibernate?
I do something like this for integration tests using specflow.
I have a NHibernateInitializer class that I inherit in all my projects that looks like this
public abstract class NHibernateInitializer : IDomainMapper
{
protected Configuration Configure;
private ISessionFactory _sessionFactory;
private readonly ModelMapper _mapper = new ModelMapper();
private Assembly _mappingAssembly;
private readonly String _mappingAssemblyName;
private readonly String _connectionString;
protected NHibernateInitializer(String connectionString, String mappingAssemblyName)
{
if (String.IsNullOrWhiteSpace(connectionString))
throw new ArgumentNullException("connectionString", "connectionString is empty.");
if (String.IsNullOrWhiteSpace(mappingAssemblyName))
throw new ArgumentNullException("mappingAssemblyName", "mappingAssemblyName is empty.");
_mappingAssemblyName = mappingAssemblyName;
_connectionString = connectionString;
}
public ISessionFactory SessionFactory
{
get
{
return _sessionFactory ?? (_sessionFactory = Configure.BuildSessionFactory());
}
}
private Assembly MappingAssembly
{
get
{
return _mappingAssembly ?? (_mappingAssembly = Assembly.Load(_mappingAssemblyName));
}
}
public void Initialize()
{
Configure = new Configuration();
Configure.EventListeners.PreInsertEventListeners = new IPreInsertEventListener[] { new EventListener() };
Configure.EventListeners.PreUpdateEventListeners = new IPreUpdateEventListener[] { new EventListener() };
Configure.SessionFactoryName(System.Configuration.ConfigurationManager.AppSettings["SessionFactoryName"]);
Configure.DataBaseIntegration(db =>
{
db.Dialect<MsSql2008Dialect>();
db.Driver<SqlClientDriver>();
db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
db.IsolationLevel = IsolationLevel.ReadCommitted;
db.ConnectionString = _connectionString;
db.BatchSize = 20;
db.Timeout = 10;
db.HqlToSqlSubstitutions = "true 1, false 0, yes 'Y', no 'N'";
});
Configure.SessionFactory().GenerateStatistics();
Map();
}
public virtual void InitializeAudit()
{
var enversConf = new Envers.Configuration.Fluent.FluentConfiguration();
enversConf.Audit(GetDomainEntities());
Configure.IntegrateWithEnvers(enversConf);
}
public void CreateSchema()
{
new SchemaExport(Configure).Create(false, true);
}
public void DropSchema()
{
new SchemaExport(Configure).Drop(false, true);
}
private void Map()
{
_mapper.AddMappings(MappingAssembly.GetExportedTypes());
Configure.AddDeserializedMapping(_mapper.CompileMappingForAllExplicitlyAddedEntities(), "MyWholeDomain");
}
public HbmMapping HbmMapping
{
get { return _mapper.CompileMappingFor(MappingAssembly.GetExportedTypes()); }
}
public IList<HbmMapping> HbmMappings
{
get { return _mapper.CompileMappingForEach(MappingAssembly.GetExportedTypes()).ToList(); }
}
/// <summary>
/// Gets the domain entities.
/// </summary>
/// <returns></returns>
/// <remarks>by default anything that derives from EntityBase and isn't abstract or generic</remarks>
protected virtual IEnumerable<System.Type> GetDomainEntities()
{
List<System.Type> domainEntities = (from t in MappingAssembly.GetExportedTypes()
where typeof(EntityBase<Guid>).IsAssignableFrom(t)
&& (!t.IsGenericType || !t.IsAbstract)
select t
).ToList();
return domainEntities;
}
}
Then in my global.asax Application_Begin event handler I configure it
public class MvcApplication : HttpApplication
{
private const String Sessionkey = "current.session";
private static IWindsorContainer Container { get; set; }
private static ISessionFactory SessionFactory { get; set; }
public static ISession CurrentSession
{
get { return (ISession) HttpContext.Current.Items[Sessionkey]; }
private set { HttpContext.Current.Items[Sessionkey] = value; }
}
protected void Application_Start()
{
Version version = Assembly.GetExecutingAssembly().GetName().Version;
Application["Version"] = String.Format("{0}.{1}", version.Major, version.Minor);
Application["Name"] = ConfigurationManager.AppSettings["ApplicationName"];
//create empty container
//scan this assembly for any installers to register services/components with Windsor
Container = new WindsorContainer().Install(FromAssembly.This());
//API controllers use the dependency resolver and need to be initialized differently than the mvc controllers
GlobalConfiguration.Configuration.DependencyResolver = new WindsorDependencyResolver(Container.Kernel);
//tell ASP.NET to get its controllers from Castle
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(Container.Kernel));
//initialize NHibernate
ConnectionStringSettings connectionString = ConfigurationManager.ConnectionStrings[Environment.MachineName];
if (connectionString == null)
throw new ConfigurationErrorsException(String.Format("Connection string {0} is empty.",
Environment.MachineName));
if (String.IsNullOrWhiteSpace(connectionString.ConnectionString))
throw new ConfigurationErrorsException(String.Format("Connection string {0} is empty.",
Environment.MachineName));
string mappingAssemblyName = ConfigurationManager.AppSettings["NHibernate.Mapping.Assembly"];
if (String.IsNullOrWhiteSpace(mappingAssemblyName))
throw new ConfigurationErrorsException(
"NHibernate.Mapping.Assembly key not set in application config file.");
var nh = new NHInit(connectionString.ConnectionString, mappingAssemblyName);
nh.Initialize();
nh.InitializeAudit();
SessionFactory = nh.SessionFactory;
AutoMapConfig.RegisterMaps();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ModelBinderConfig.RegisterModelBinders(ModelBinders.Binders);
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
}
protected void Application_OnEnd()
{
//dispose Castle container and all the stuff it contains
Container.Dispose();
}
protected void Application_BeginRequest() { CurrentSession = SessionFactory.OpenSession(); }
protected void Application_EndRequest()
{
if (CurrentSession != null)
CurrentSession.Dispose();
}
}
}

conditional DI with the help of config file

How can we achieve conditional Dependency Injection with the help of Unity Application block config file ? Below is my piece of code.
namespace DependencyInjection
{
//UI
class Program
{
static void Main(string[] args)
{
IUnityContainer objContainer = new UnityContainer();
objContainer.LoadConfiguration(); //loads from app
Customer obj = objContainer.Resolve<Customer>();
obj.CustomerName = "test1";
obj.Add();
}
}
//ML
public class Customer
{
private IDAL Odal;
public string CustomerName { get; set; }
public Customer(IDAL iobj)
{
Odal = iobj;
}
public void Add()
{
Odal.Add();
}
}
//DAL
public interface IDAL
{
void Add();
}
public class SQLServerDAL:IDAL
{
public void Add()
{
Console.WriteLine("SQL Server inserted");
Console.ReadLine();
}
}
public class OracleDAL:IDAL
{
public void Add()
{
Console.WriteLine("Oracle inserted");
Console.ReadLine();
}
}
}
And my config file is like below:
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<container>
<register type="DependencyInjection.IDAL, DependencyInjection"
mapTo="DependencyInjection.SQLServerDAL,DependencyInjection"/>
</container>
</unity>
How can I achieve the following :
If(somecondition=true)
Customer object should resolve to SQLServerDal
Else
Customer object should resolve to OracleDal
Is it possible ? If yes, how ?

Integration test with IOptions<> in .NET Core

I pass IOption<T> to my CommandBus so I can get the settings from my ServiceBusSetting class. I want to do an integration test of my Bus. I do not want to resolve it just use new QueueCommandBus and need to pass IOptions to it.
var services = new ServiceCollection().AddOptions();
services.Configure<ServiceBusAppSettings>(Configuration.GetSection("ServiceBus"));
var options = services.BuildServiceProvider().GetService<IOptions<ServiceBusAppSettings>>();
////Act
var commandBus = new QueueCommandBus(options);
This works fine, but feels very complex code to get the IOptions<T> from my appsetting.json in my test project.
Any clue if this is the only way or is there a better way?
You don't need to create the ServiceCollection or IServiceProvider. The IConfiguration interface has a Bind() method, or from .NET Core 1.1 onwards, Get<T> which you can use to get the strongly-typed object directly:
var config = Configuration.GetSection("ServiceBus");
// .NET Core 1.0
var options = new ServiceBusAppSettings();
config.Bind(options);
// .NET Core 1.1
var options = config.Get<ServiceBusAppSettings>();
I like to add these as static methods to my AppSettings strongly-typed object, to make it convenient to load them from JSON in both my web app and from unit tests.
AppSettings.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
namespace My.Namespace
{
public class AppSettings
{
public class ServiceBusAppSettings
{
public string Setting1;
public int Setting2;
}
public class ApiSettings
{
public bool FormatJson { get; set; }
}
public class MySqlSettings
{
public string User { get; set; }
public string Password { get; set; }
public string Host { get; set; }
public string Database { get; set; }
public int Port { get; set; } = 3306;
public string GetConnectionString()
{
return $"Server={Host};Database={Database};Port={Port};Uid={User};Pwd={Password}";
}
}
public ServiceBusAppSettings ServiceBus { get; set; } = new ServiceBusAppSettings();
public ApiSettings Api { get; set; } = new ApiSettings();
public MySqlSettings MySql { get; set; } = new MySqlSettings();
// Static load helper methods. These could also be moved to a factory class.
public static IConfigurationRoot GetConfiguration(string dir)
{
return GetConfiguration(dir, null);
}
public static IConfigurationRoot GetConfiguration(string dir, string environmentName)
{
if (string.IsNullOrEmpty(environmentName))
environmentName = "Development";
var builder = new ConfigurationBuilder()
.SetBasePath(dir)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{environmentName}.json", true)
.AddEnvironmentVariables();
return builder.Build();
}
public static AppSettings GetSettings(string dir)
{
return GetSettings(dir, null);
}
public static AppSettings GetSettings(string dir, string environmentName)
{
var config = GetConfiguration(dir, environmentName);
return GetSettings(config);
}
public static AppSettings GetSettings(IConfiguration config)
{
return config.Get<AppSettings>();
}
}
}
ASP.NET Core Startup.cs: (Getting the strongly-typed settings object is often helpful at this stage, when configuring the other services...)
public class Startup
{
public Startup(IHostingEnvironment env)
{
Configuration = AppSettings.GetConfiguration(env.ContentRootPath, env.EnvironmentName);
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Configure the service collection.
services.AddOptions();
services.Configure<AppSettings>(Configuration);
// It can also be handy to get the AppSettings object here.
var settings = AppSettings.GetSettings(Configuration);
// Add framework services.
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
// Pretty-print JSON in Development
options.SerializerSettings.Formatting = settings.Api.FormatJson ? Formatting.Indented : Formatting.None;
});
// Store DB connection info in AppSettings too...
var conn = settings.MySql.GetConnectionString();
services.AddDbContext<MyDbContext>(opt => opt.UseMySql(conn));
}
}
In Test Class:
var testDir = AppContext.BaseDirectory;
var settings = AppSettings.GetSettings(testDir, "Test");
//Act
var commandBus = new QueueCommandBus(settings);

StructureMap Question

This is the equivalent of what I'm trying to create with StructureMap:
new ChangePasswordWithNotificationAndLoggingService(
new ChangePasswordService(
new ActiveDirectoryRepository(new ActiveDirectoryCredentials()),
new TokenRepository("")),
new EmailNotificationService(new PasswordChangedNotification(new UserAccount())),
new LoggingService());
This is what I have right now:
ForRequestedType<IChangePasswordService>()
.TheDefault.Is.ConstructedBy(() =>
new ChangePasswordService(DependencyRegistrar.Resolve<IActiveDirectoryRepository>(),
DependencyRegistrar.Resolve<ITokenRepository>()))
.EnrichWith<IChangePasswordService>(x =>
new ChangePasswordWithNotificationAndLoggingService(x,
DependencyRegistrar.Resolve<INotificationService>(),
DependencyRegistrar.Resolve<ILoggingService>()));
I need to pass the UserAccount to the INotificationService...can't figure it out.
I've tried this:
DependencyRegistrar.With(new UserAccount { Username = "test" });
No luck...UserAccount always turns out null. I don't have to do it all with StructureMap, I'm open to any suggestions.
This is what I currently have working:
public static IChangePasswordService ChangePasswordService(UserAccount userAccount)
{
return new ChangePasswordWithNotificationService(
new ChangePasswordService(ActiveDirectoryRepository(), TokenRepository()),
new EmailNotificationService(new PasswordChangedNotification(userAccount)));
}
Have you tried just using AutoWiring? These are all concrete classes with simple construction so StructureMap can figure out what you need.
For<IChangePasswordService>().Use<ChangePasswordService>();
Looking at your construction I think that this simple configuration might just work.
Edit
Regarding the comments.
You should use the With(T instance) method to have the container construct your IChangePasswordService using the given userAccount.
var userAccount = new UserAccount("derans");
var changePasswordService = container.With(userAccount).GetInstance<IChangePasswordService>();
Why not encapsulate the creation of the change password service into a factory - the factory is then an implemented as StructureMap factory that use a UserAccount passed in and the 'ObjectFactory' to create instances of the IIChangePasswordService as required?
I have demo'ed it below:
namespace SMTest
{
class Program
{
static void Main(string[] args)
{
// bootstrapper...
ObjectFactory.Configure(x => x.AddRegistry(new TestRegistry()));
// create factory for use later (IoC manages this)...
var changePasswordServiceFactory = ObjectFactory.GetInstance<IChangePasswordServiceFactory>();
var daveAccount = new UserAccount("Dave Cox");
var steveAccount = new UserAccount("Steve Jones");
var passwordService1 = changePasswordServiceFactory.CreateForUserAccount(daveAccount);
var passwordService2 = changePasswordServiceFactory.CreateForUserAccount(steveAccount);
}
}
public class TestRegistry : Registry
{
public TestRegistry()
{
Scan(x =>
{
x.TheCallingAssembly();
x.AssemblyContainingType(typeof(IChangePasswordService));
x.AssemblyContainingType(typeof(IActiveDirectoryRepository));
x.AssemblyContainingType(typeof(IActiveDirectoryCredentials));
x.AssemblyContainingType(typeof(ITokenRepository));
x.AssemblyContainingType(typeof(INotification));
x.AssemblyContainingType(typeof(INotificationService));
x.AssemblyContainingType(typeof(ILoggingService));
ForRequestedType<ILoggingService>().TheDefault.Is.OfConcreteType<MyLogger>();
ForRequestedType<IActiveDirectoryRepository>().TheDefault.Is.OfConcreteType<MyAdRepository>();
ForRequestedType<IActiveDirectoryCredentials>().TheDefault.Is.OfConcreteType<MyAdCredentials>();
ForRequestedType<ITokenRepository>().TheDefault.Is.OfConcreteType<MyTokenRepository>();
ForRequestedType<IChangePasswordService>().TheDefault.Is.OfConcreteType<ChangePasswordService>();
ForRequestedType<IChangePasswordServiceFactory>().CacheBy(InstanceScope.Singleton).TheDefault.Is.OfConcreteType<StructureMapChangePasswordServiceFactory>();
ForRequestedType<INotification>().TheDefault.Is.OfConcreteType<MyPasswordChangedNotification>();
ForRequestedType<INotificationService>().TheDefault.Is.OfConcreteType<MyEmailNotificationService>();
});
}
}
public interface ILoggingService
{
}
public class MyLogger : ILoggingService
{
}
public class UserAccount
{
public string Name { get; private set; }
public UserAccount(string name)
{
Name = name;
}
}
public interface INotification
{
}
public class MyPasswordChangedNotification : INotification
{
private readonly UserAccount _account;
private readonly ILoggingService _logger;
public MyPasswordChangedNotification(UserAccount account, ILoggingService logger)
{
_account = account;
_logger = logger;
}
}
public interface INotificationService
{
}
public class MyEmailNotificationService : INotificationService
{
private readonly INotification _notification;
private readonly ILoggingService _logger;
public MyEmailNotificationService(INotification notification, ILoggingService logger)
{
_notification = notification;
_logger = logger;
}
}
public interface ITokenRepository
{
}
public class MyTokenRepository : ITokenRepository
{
}
public interface IActiveDirectoryRepository
{
}
public interface IActiveDirectoryCredentials
{
}
public class MyAdCredentials : IActiveDirectoryCredentials
{
}
public class MyAdRepository : IActiveDirectoryRepository
{
private readonly IActiveDirectoryCredentials _credentials;
public MyAdRepository(IActiveDirectoryCredentials credentials)
{
_credentials = credentials;
}
}
public interface IChangePasswordService
{
}
public class ChangePasswordService : IChangePasswordService
{
private readonly IActiveDirectoryRepository _adRepository;
private readonly ITokenRepository _tokenRepository;
private readonly INotificationService _notificationService;
public ChangePasswordService(IActiveDirectoryRepository adRepository, ITokenRepository tokenRepository, INotificationService notificationService)
{
_adRepository = adRepository;
_tokenRepository = tokenRepository;
_notificationService = notificationService;
}
}
public interface IChangePasswordServiceFactory
{
IChangePasswordService CreateForUserAccount(UserAccount account);
}
public class StructureMapChangePasswordServiceFactory : IChangePasswordServiceFactory
{
public IChangePasswordService CreateForUserAccount(UserAccount account)
{
return ObjectFactory.With(account).GetInstance < IChangePasswordService>();
}
}
}

Resources