In ninject I have code like this:
var resourceManagers = new ResourceManager[1];
resourceManagers[0] = Validation.ResourceManager;
kernel.Bind<ILocalizedStringProvider>().To<ResourceStringProvider>()
.WithConstructorArgument("resourceManager", resourceManagers);
kernel.Rebind<ModelValidatorProvider>().To<LocalizedModelValidatorProvider>();
I want to convert this to StructureMap
I did like this:
IContainer container = new Container();
var ioC = new IoC();
ioC.Initialize(container);
container.Configure(x =>
{
var resourceManagers = new ResourceManager[1];
resourceManagers[0] = ModelValidation.ResourceManager;
x.For<ILocalizedStringProvider>().Use<ResourceStringProvider>.Ctor<string>(#"resourceManager").Is(resourceManagers);
x.For<ModelValidatorProvider>().Add<LocalizedModelValidatorProvider>();
});
DependencyResolver.SetResolver(new StructureMapDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new StructureMapDependencyResolver(container);
but I get exception:
Severity Code Description Project File Line Suppression State
Error CS0119 'CreatePluginFamilyExpression.Use()'
is a method, which is not valid in the given context
How to do this with StructureMap?
Use () because is a method
var resourceManagers = new ResourceManager[1];
resourceManagers[0] = ModelValidation.ResourceManager;
x.For<ILocalizedStringProvider>().Use<ResourceStringProvider>().Ctor<ResourceManager[]> (#"resourceManager").Is(resourceManagers);
x.For<ModelValidatorProvider>().Add<LocalizedModelValidatorProvider>();
As the error message says, this:
.Use<ResourceStringProvider>
Should be:
.Use<ResourceStringProvider>()
Because it is a method.
Related
While upgrading from NLog 4.7.15 to 5.0.1 I found this test in our code base:
[Test]
public void CustomLogFactoryShouldBehaveCompletelyIndependent()
{
var memoryTarget4 = new MemoryTarget();
memoryTarget4.Layout = "${level}|${logger}|${message}${exception}";
var memoryTarget5 = new MemoryTarget();
var customConfig = new LoggingConfiguration();
customConfig.AddTarget("UnitTestLogger4", memoryTarget4);
customConfig.LoggingRules.Add(new LoggingRule("UnitTestLogger4", LogLevel.Trace, memoryTarget4));
customConfig.AddTarget("UnitTestLogger2", memoryTarget5);
customConfig.LoggingRules.Add(new LoggingRule("UnitTestLogger2", LogLevel.Info, memoryTarget5));
using (var customFactory = new LogFactory(customConfig)) // <<-- This ctor is marked obsolete
{
// Log logger defined only in custom config
var logger4 = customFactory.GetLogger("UnitTestLogger4");
logger4.Trace("Test4");
Assert.That(memoryTarget4.Logs.Count, Is.EqualTo(1));
Assert.That(memoryTarget5.Logs, Is.Empty);
memoryTarget4.Logs.Clear();
//... More cases tested here
}
}
The test works fine both in the old and the new version, but only after I disable the deprecation warning CS0618 for the constructor LogFactory(LoggingConfiguration).
To work around this, I tried to use the suggested alternative LoggingConfiguration(LogFactory), which connects the factory and the configuration basically the other way round.
[Test]
public void CustomLogFactoryShouldBehaveCompletelyIndependent()
{
var memoryTarget4 = new MemoryTarget();
memoryTarget4.Layout = "${level}|${logger}|${message}${exception}";
var memoryTarget5 = new MemoryTarget();
using (var customFactory = new LogFactory())
{
var customConfig = new LoggingConfiguration(customFactory);
customConfig.AddTarget("UnitTestLogger4", memoryTarget4);
customConfig.LoggingRules.Add(new LoggingRule("UnitTestLogger4", LogLevel.Trace, memoryTarget4));
customConfig.AddTarget("UnitTestLogger2", memoryTarget5);
customConfig.LoggingRules.Add(new LoggingRule("UnitTestLogger2", LogLevel.Info, memoryTarget5));
// customFactory.ReconfigExistingLoggers(); // <<-- Adding this changes nothing
// Log logger defined only in custom config
var logger4 = customFactory.GetLogger("UnitTestLogger4");
logger4.Trace("Test4");
Assert.That(memoryTarget4.Logs.Count, Is.EqualTo(1)); // <<-- Fails here, nothing was added to memoryTarget4.
Assert.That(memoryTarget5.Logs, Is.Empty);
memoryTarget4.Logs.Clear();
}
}
At least that's what I think should be changed. But the test fails now. The configuration is not applied, as the target does not get any logs.
What did I miss? What's the correct way of replacing the deprecated LogFactory(LoggingConfiguration) constructor here?
The constructor new LogFactory(customConfig) became obsolete to ensure that the LoggingConfiguration.Factory-option had the expected value (Using the intended isolated LogFactory instead of the global static LogManager.Factory)
You can replace:
customFactory.ReconfigExistingLoggers();
With this so the configuration is activated:
customFactory.Configuration = customConfig;
Alternative you could do this:
using var customFactory = new NLog.LogFactory().Setup().LoadConfiguration(builder => {
var memoryTarget4 = new MemoryTarget();
memoryTarget4.Layout = "${level}|${logger}|${message}${exception}";
var memoryTarget5 = new MemoryTarget();
builder.Configuration.LoggingRules.Add(new LoggingRule("UnitTestLogger4", LogLevel.Trace, memoryTarget4));
builder.Configuration.LoggingRules.Add(new LoggingRule("UnitTestLogger2", LogLevel.Info, memoryTarget5)));
}).LogFactory;
var logger4 = customFactory.GetLogger("UnitTestLogger4");
logger4.Trace("Test4");
Assert.That(memoryTarget4.Logs.Count, Is.EqualTo(1));
Assert.That(memoryTarget5.Logs, Is.Empty);
memoryTarget4.Logs.Clear();
I have a .NET 4.5.2 migration runner built using FluentMigrator 2.0.7 that "m trying to move to .NET 5.0 and FluentMigrator 3.2.15.
My current difficulty is writing the output to a text file.
var serviceProvider = new ServiceCollection()
.AddSingleton<ILoggerProvider, SqlScriptFluentMigratorLoggerProvider>()
.Configure<LogFileFluentMigratorLoggerOptions>(o => {
o.OutputFileName = "MyFilename.log";
})
.AddLogging(lb => lb.AddFluentMigratorConsole())
.Configure<FluentMigratorLoggerOptions>(o =>
{
o.ShowSql = true;
o.ShowElapsedTime = true;
})
.AddFluentMigratorCore()
.ConfigureRunner(builder =>
builder
.AddSqlServer2016()
.WithGlobalConnectionString(this.options.connectionString.ExpandDataDirectory())
.WithMigrationsIn(this.assembly)
)
.BuildServiceProvider();
using (var scope = serviceProvider.CreateScope())
{
var runner = scope.ServiceProvider.GetRequiredService<IMigrationRunner>();
if (this.options.reverseMigration)
runner.MigrateDown(0);
else
runner.MigrateUp();
}
My problem is simple - when I try to run the migration I get an error:
Unable to resolve service for type 'System.IO.TextWriter' while attempting to activate 'FluentMigrator.Runner.Logging.SqlScriptFluentMigratorLoggerProvider'.
What's going on is simple enough - TextWriter is an abstract class, it can't be initiated.
But how do I configured the ServiceProvider so that when it's asked for a TextWriter, it returns a StreamWriter writing to the OutputFileName I provided to LogFileFluentMigratorLoggerOptions?
===
Edited:
I can do this:
using var logStream = new FileStream(this.options.outputFilename, FileMode.Append);
using var sw = new StreamWriter(logStream);
var serviceProvider = new ServiceCollection()
.AddSingleton<TextWriter>(sw)
.AddSingleton<ILoggerProvider, SqlScriptFluentMigratorLoggerProvider>()
...
But it strikes me as being very ugly...
I could write an sql output to a text file by registering LogFileFluentMigratorLoggerProvider as a singleton and then configuring it. LogFileFluentMigratorLoggerProvider inherits from SqlScriptFluentMigratorLoggerProvider and takes care to instantiate a StreamWriter for you.
private static IServiceProvider CreateServices()
{
// var connectionString = ""; grab your connection string
return new ServiceCollection()
.AddFluentMigratorCore()
.ConfigureRunner(rb => rb
.AddSqlServer2016()
.WithGlobalConnectionString(connectionString)
.WithMigrationsIn(this.assembly))
.AddLogging(lb => lb.AddFluentMigratorConsole())
.AddSingleton<ILoggerProvider, LogFileFluentMigratorLoggerProvider>()
.Configure<LogFileFluentMigratorLoggerOptions>(
opt =>
{
opt.OutputFileName = "C:\\TEMP\\DatabaseMigration.sql";
opt.OutputGoBetweenStatements = true;
opt.ShowSql = true;
})
.BuildServiceProvider(false);
}
I'm new to using DI in C# and had a look at Windsor, Ninject, Autofac, Unity and Simple Injector. I originally discarded Simple Injector because I needed value-type injection (fx. connection strings) but found a blogpost describing this: https://cuttingedge.it/blogs/steven/pivot/entry.php?id=94. Unfortunately the blogpost is outdated since IDependencyInjectionBehavior.BuildExpression is deprecated in version 4.1 and IDependencyInjectionBehavior.GetInstanceProducer has been introduced instead.
I'm not sure how to do what the blogpost describes with the new InstanceProducer. InstanceProducer has a static method FromExpression but I'm not sure which type etc. should be used.
I currently do Pure DI and have the following settings:
// Settings
var conLocal = ConfigurationManager.ConnectionStrings["APIPortMan"].ConnectionString;
var con = ConfigurationManager.ConnectionStrings["PortMan"].ConnectionString;
var conAzure = ConfigurationManager.ConnectionStrings["Azure"].ConnectionString;
var conSitecore = ConfigurationManager.ConnectionStrings["Sitecore"].ConnectionString;
var azureStorageAccount = ConfigurationManager.AppSettings.Get("StorageConnection");
var reportUploadPath = ConfigurationManager.AppSettings.Get("ReportUploadPath");
var PfsmlPath = ConfigurationManager.AppSettings.Get("PfsmlPath");
var reloadCounter = int.Parse(ConfigurationManager.AppSettings.Get("transactionServiceReloadCounter"));
var systemStartDate = DateTime.Parse(ConfigurationManager.AppSettings.Get("holdingServiceStartDate"));
var semaphoreCount = int.Parse(ConfigurationManager.AppSettings.Get("semaphoreCount"));
Then I have some repositories consuming these settings:
// Repositories
var _accountRepository = new AccountRepository(con, conAzure);
var _aggregatedPortfolioRelationshipRepository = new AggregatedPortfolioRelationshipRepository(conAzure);
var _aggregatedClientRelationshipRepository = new AggregatedClientRelationshipRepository(con, conAzure);
var _assetBondRepository = new AssetBondRepository(con, conAzure);
var _assetClassRepository = new AssetClassRepository(con, conAzure);
var _assetDerivativeRepository = new AssetDerivativeRepository(con, conAzure);
var _assetRepository = new AssetRepository(con, conAzure);
var _benchmarkRepository = new BenchmarkRepository(con, conAzure);
var _benchmarkWeightRepository = new BenchmarkWeightRepository(con, conAzure);
var _clientRepository = new ClientRepository(con, conAzure);
var _defaultPriceRepository = new DefaultPriceRepository(con, conAzure);
var _emailRepository = new UpdateEmailOutput(conAzure);
var _exchangeRateRepository = new ExchangeRateRepository(con, conAzure);
var _failedHoldingRepository = new FailedHoldingRepository(conLocal);
var _GICSRepository = new GICSRepository(con, conAzure);
var _holdingRepository = new HoldingRepository(conAzure);
var _limitLineRepository = new LimitLineRepository(con, conAzure);
var _PFSMLRepository = new PFSMLRepository(PfsmlPath);
var _portfolioRepository = new PortfolioRepository(con, conAzure);
var _sitecoreReportRepository = new SitecoreReportRepository(conSitecore, reportUploadPath);
var _systemInfoRepository = new SystemInfoRepository(conAzure);
var _transactionRepository = new TransactionRepository(con, conAzure);
And later some services consuming the repositories and a few of the settings.
Since most of the repositories share a common interface IRepository<T> (besides an individual interface like IAssetRepository that extends IRepository with the type Asset) I would like to be able to use batch creation /auto-wiring. Also I would like to avoid changing the DI setup every time a change is made to my main code (ie. new constructor parameter, new repository interface/class etc.). Also I would like to avoid using lambda's, "new" and getInstance in the registration process because this will require changes to the DI setup whenever I change a constructor.
I have already adopted the convention mentioned in the blogpost (using AzureConnectionString, PortManConnectionString etc.) in the constructors. All I need to do now is make sure Simple Injector handles value type parameters according to the conventions :)
I posted the same question on Github (as Steven noted).
https://github.com/simpleinjector/SimpleInjector/blob/v4.0.x/src/SimpleInjector.CodeSamples/ParameterConventionExtensions.cs contains an updated version of the convention-based approach although Steven/dotnetjunkie convinced me to take a different approach (using settings-objects).
I am trying to add description when attaching a file and I use MVVM Model. I create a commandHolder and from there create a command and I unable to convert a Lazy<View> to Lazy<RelayCommand> I request you to please help me in this.
this.fileAttachmentDescriptionCommandHolder = new Lazy<FileAttachmentDescriptionView>(() => new FileAttachmentDescriptionView { DataContext = this });
this.fileAttachmentDescriptionViewHolder = new Lazy<RelayCommand>(this.CreateFileAttachmentDescriptionCommand);
I get the error:
Error CS0029 Cannot implicitly convert type 'System.Lazy
Path.RelayCommand' to 'System.Lazy path.View'
I think you're doing it backwards, as stated in the comment by mechanic.
this.fileAttachmentDescriptionCommandHolder =
new Lazy<RelayCommand>(this.CreateFileAttachmentDescriptionCommand);
this.fileAttachmentDescriptionViewHolder =
new Lazy<FileAttachmentDescriptionView>(
() => new FileAttachmentDescriptionView { DataContext = this });
My goal is to vary a string parameter:
Container
.RegisterInstance<string>("us", #"\\ad1\accounting$\Xml\qb_us.xml")
.RegisterInstance<string>("intl", #"\\ad1\accounting$\Xml\qb_intl.xml");
driver = Container.Resolve<LoaderDriver>(args[1]); // "us" or "intl"
Which results in:
Resolution of the dependency failed, type = "QuickBooksService.LoaderDriver", name = "intl".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type String cannot be constructed. You must configure the container to supply this value.
-----------------------------------------------
At the time of the exception, the container was:
Resolving QuickBooksService.LoaderDriver,intl
Resolving parameter "reader" of constructor QuickBooksService.LoaderDriver(QuickBooksService.LoaderInputReader reader, QuickBooksService.ILoader[] loaders)
Resolving QuickBooksService.LoaderInputReader,(none)
Resolving parameter "inputFile" of constructor QuickBooksService.LoaderInputReader(System.String inputFile, AccountingBackupWeb.Models.AccountingBackup.Company company, Qu
ickBooksService.eTargets targets)
Resolving System.String,(none)
This is obviously wrong but its the only way I could get it to work:
if (args[1] == "us")
Container
.RegisterType<LoaderInputReader>(
new InjectionConstructor(
#"\\ad1\accounting$\Xml\qb_us.xml",
new ResolvedParameter<Company>(),
new ResolvedParameter<eTargets>()
)
)
;
else if (args[1] == "intl")
Container
.RegisterType<LoaderInputReader>(
new InjectionConstructor(
#"\\ad1\accounting$\Xml\qb_intl.xml",
new ResolvedParameter<Company>(),
new ResolvedParameter<eTargets>()
)
)
;
else
throw new Exception("invalid company");
driver = Container.Resolve<LoaderDriver>();
Something like this ought to work:
container
.RegisterType<LoaderInputReader>(
"us",
new InjectionConstructor(
#"\\ad1\accounting$\Xml\qb_us.xml",
new ResolvedParameter<Company>(),
new ResolvedParameter<eTargets>()));
container
.RegisterType<LoaderInputReader>(
"intl",
new InjectionConstructor(
#"\\ad1\accounting$\Xml\qb_intl.xml",
new ResolvedParameter<Company>(),
new ResolvedParameter<eTargets>()));
This names each registration of LoaderInputReader. Now you can resolve like this:
var us = container.Resolve<LoaderInputReader>("us");
var intl = container.Resolve<LoaderInputReader>("intl");
Perhaps you could change
driver = Container.Resolve<LoaderDriver>(args[1]); // "us" or "intl"
with
driver = Container.Resolve<LoaderDriver>(Container.Resolve<string>(args[1]))
This takes advantage of the Resolve overload that takes a name, where in your case the name comes from your argument.