I'm working on a existing Windows Phone project and want to use the IOC container from MVVMCross, but not the other extra features (yet).
I installed MVVMCross.Core 4.x and try to use 'ConstructAndRegisterSingleton' from the App() constructor of the Windows app, but it throws an Null ref exception.
Tried to find any bootstrapper, setup or initialization for MVVMCross but can't find any in the new 4.x core.
Anyone any idea?
Found it.... and it seems to work.
Just get MVVMCross.Core from Nuget and create a setup like:
internal static class Setup
{
public static void InitializeIoc()
{
CreateIocProvider();
// Register all services
Mvx.ConstructAndRegisterSingleton<ILoudnessLimitsRegulator, LoudnessLimitsRegulator>();
}
private static void CreateIocProvider()
{
// Ioc options
var options = new MvxIocOptions();
// initialize the IoC registry, then add it to itself
var iocProvider = MvxSimpleIoCContainer.Initialize(options);
Mvx.RegisterSingleton(iocProvider);
}
}
Related
Having successfully created and run a simple Azure Durable Function in Visual Studio 2017, I want to introduce logging.
The Visual Studio project template generates the static HttpStart class with a
Run method containing an optional parameter of type Microsoft.Extensions.Logging.ILogger.
I have no idea how to hook up dependency injection into a durable function project. Can anyone point me to an example on how to achieve this?
It looks to me like I will need some class, inside which I will need to use the Microsoft.Extensions.Logging.LoggingFactory.CreateLogger() method.
I imagine that this logic will need to be within a container class that is hooked into the run time pipeline using HostBuilder in some way (similar to using WebHostBuilder in static main method).
Thank you
#Thomas pointed me to examples I used to extract code that allows dependency injection to work when writing a durable function project:
using System.IO;
using System.Reflection;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Extensions.Logging;
using tmetadastoreFnApp;
using Willezone.Azure.WebJobs.Extensions.DependencyInjection;
using ILogger = Microsoft.Extensions.Logging.ILogger;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
[assembly: WebJobsStartup(typeof(Startup))]
namespace tmetadastoreFnApp
{
internal class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder) =>
builder.AddDependencyInjection(ConfigureServices);
private void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ILoggerFactory, LoggerFactory>();
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
services.AddLogging((builder) => builder.SetMinimumLevel(LogLevel.Trace));
var serviceProvider = services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
loggerFactory.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true });
var dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
LogManager.LoadConfiguration(Directory.GetParent(dir) + "\\nlog.config");
}
}
}
We've been using MVVMCross for the 18 months. Great Stuff. But, we're looking to migrate from Xamarin.iOS to Xamarin.Forms in an effort to speed development time.
We have a PCL with our ViewModels. But, would like to have our View (Pages) in a separate PCL library, to allow parallel development with Native application.
MVVMCross can not seem to locate the Page if it's located in a separate PCL library, OR if it's located in the Application. However, if I put the Page in the same PCL as the ViewModels, things work like a champ.
I've tried putting the following code in our Setup.cs
protected override IEnumerable<Assembly> GetViewAssemblies()
{
var list = new List<Assembly>();
list.AddRange(base.GetViewAssemblies());
list.Add(typeof(NuSales.Forms.Pages.TestPage).GetTypeInfo().Assembly);
return list;
}
But, still no joy.
Any hints on how to fix the resolver to find the View (Page)?
Thanks
Looking at https://github.com/MvvmCross/MvvmCross-Forms/blob/master/MvvmCross.Forms.Presenter.Core/MvxFormsPageLoader.cs#L44
protected virtual Type GetPageType(string pageName)
{
return _request.ViewModelType.GetTypeInfo().Assembly.CreatableTypes()
.FirstOrDefault(t => t.Name == pageName);
}
... I'd say you need to override the default IMvxFormsPageLoader to change that single Assembly lookup.
...or (for bonus points) you could send in a Pull Request that changes the default behaviour to use the view assemblies collection - and it could also store a Dictionary to avoid multiple Reflection passes and to speed up lookup times.
Hopefully, I'm doing this right in terms of StackOverflow etiquette. Using Stuart's suggestion... A quick fix is.
Create a FormPageLoader like below.
public class MyFormsPageLoader : MvxFormsPageLoader
{
public MyFormsPageLoader() {
}
protected override Type GetPageType(string pageName)
{
return typeof(NuSales.Forms.Pages.TestPage).GetTypeInfo().Assembly.CreatableTypes().FirstOrDefault(t => t.Name == pageName);
}
}
Then you need to register it. I did it in my App.Initialize code
public class FormsApp : MvxApplication
{
public override void Initialize()
{
base.Initialize();
Mvx.RegisterSingleton(typeof(IMvxFormsPageLoader), new MyFormsPageLoader());
RegisterAppStart<TestViewModel>();
}
}
I'm evaluating asp.net core and .net core and I'm not yet sure about some things. In the past it was possible to configure many components using the web.config out of the box.
To name some examples:
There was the membership-provider and I could implement many providers but I was able ton configure later which provider should be used. This was dependend of the use-case. Now I should use asp.net identity - but I can only find configurations that are performed in sourcecode.
Same for authentication. I can define "CookieAuthentication" and have to set the name, loginpath or the timeout within sourcecode. In the past I was able to set timeout, etc... via web.config.
Is there any way to configure partially these things out of the box from a config-file? Or is this not supported anymore and I have to implement this configuration on my own? In the past this was a really comfortable way.
In ASP.NET Core, Web.config file is used ONLY for IIS configuration, you cannot use it for application configuration, but there are new, better, more flexible configuration options that you can use.
There are multiple configuration sources that you can use, but in this example I'm using json. These examples are from working code in my SimpleAuth project.
You can configure things in startup from configuration files.
First you add a config file in json format that maps to your class. You can see my example class here, and the json file it maps from here
builder.AddJsonFile("simpleauthsettings.json", optional: true);
Then, in the ConfigureServices method you configure your class to be wired up from the config system as shown
services.Configure<SimpleAuthSettings>(Configuration.GetSection("SimpleAuthSettings"));
Then you add an IOptions accessor of your class to the method signature of the Configure method in the Startup.cs
The Dependency Injection will inject it into that method for you so you can use it there to configure things. Specifically I'm setting the cookie authentication scheme and name from my settings object.
The noteworthy part is that you can add whatever you want to the Configure method signature, and as long as it is something that has been registered in the ConfigureServices method, the DI will be able to inject it for you.
public class Startup
{
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
// this file is the custom configuration file to hydrate my settings from
builder.AddJsonFile("simpleauthsettings.json", optional: true);
....
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
....
services.Configure<SimpleAuthSettings>(Configuration.GetSection("SimpleAuthSettings"));
....
}
// note that the DI can inject whatever you need into this method signature
// I added IOptions<SimpleAuthSettings> authSettingsAccessor to the method signature
// you can add anything you want as long as you register it in ConfigureServices
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
IOptions<SimpleAuthSettings> authSettingsAccessor
)
{
...
// Add cookie-based authentication to the request pipeline
SimpleAuthSettings authSettings = authSettingsAccessor.Value;
var ApplicationCookie = new CookieAuthenticationOptions
{
AuthenticationScheme = authSettings.AuthenticationScheme,
CookieName = authSettings.AuthenticationScheme,
AutomaticAuthenticate = true,
AutomaticChallenge = true,
LoginPath = new PathString("/Login/Index"),
Events = new CookieAuthenticationEvents
{
//OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
}
};
app.UseCookieAuthentication(ApplicationCookie);
// authentication MUST be added before MVC
app.UseMvc();
}
}
Recently upgraded to version 3.2.1 of castle windsor and receiving an error when attempting to resolve a service that previously didn't occur in version 3.0 of the windsor framework.
IWindsorContainer container = new WindsorContainer();
The following code no longer works
// Throws component not found exception
InstallerHelper.ProcessAssembliesInBinDirectory(
assembly => container.Register(
Classes
.FromAssembly(assembly)
.BasedOn<IWindsorInstaller>()
.WithService.FromInterface()
.LifestyleSingleton()
));
var installers = container.ResolveAll<IWindsorInstaller>();
container.Install(installers);
// Fails here, is it related to a hashcode mismatch in SimpleTypeEqualityComparer?
var credentialCache = container.Resolve<ICredentialCache>()
// works fine if explicity install installers individually
container.Install(new CredentialsInstaller());
var credentialCache = container.Resolve<ICredentialCache>()
Where ProcessAssembliesInBinDir is:
public static void ProcessAssembliesInBinDirectory(Action<Assembly> action)
{
var directoryName = GetDirectoryName();
foreach (var dll in Directory.GetFiles(directoryName, "*.dll"))
{
var fileInfo = new FileInfo(dll);
if (!IgnoreList.Any(x=>fileInfo.Name.StartsWith(x)))
{
var assembly = Assembly.LoadFile(dll);
action(assembly);
}
}
}
Where credential installer is:
public class CredentialsInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<ICredentidalCache>()
.ImplementedBy<CredentidalCache>()
.LifestyleSingleton()
);
// This works fine
var credentialCache = container.Resolve<ICredentialCache>()
}
}
Class implementation
public interface ICredentidalCache {}
public class CredentidalCache : ICredentidalCache{}
This is being run from an MVC application
version 4.5 of the .net framework
the credential installer lives inside another assembly, referenced by the website
using the Windsor source, the successful attempt to resolve occurs when the typeof(ICredentialCache).GetHashCode() is the same as what has been registered. For some reason when returning out of the installer the hashcode has changed for the type. Putting a debug line inside SimpleTypeEqualityComparer.GetHashCode(Type obj) shows that hashcodes are different for the same Type.
inspecting the container inside the debugger shows the ICredentialCache successfully installed.
Edit
Manage to move forward by manually registering installers, ie. not relying on the resolve<IwindsorInstaller>() and use container.install(new Installer(), ...). If i find out more I'll update the SO question.
This works fine for me:
public sealed class AppServiceFactory
{
...
public T Create<T>()
{
return (T)container.Resolve(typeof(T));
}
...
}
AppServiceFactory.Instance.Create<IYourService>();
The problem is caused by the InstallerHelper and how it goes about loading an assembly. This SO post pointed me in the right direction,
https://stackoverflow.com/a/6675227/564957
essentially the way the assembly was loaded was failing using Assembly.LoadFile(string fileName) was causing the problem, changing this to be Assembly.Load(string assemblyName) rectified the issue.
#Eric Lippert does a good job explaining
[when] loading an assembly by its path, and one via loading the same
assembly by its assembly name... reflection will
consider types from the two loadings of the same assembly to be
different types. Any assembly loaded from its path is considered to be
distinct from an assembly loaded by its assembly name.
I've read a lot of examples/tutorials (incl. Ayende's Alexandria on MSDN).
But just getting somewhat updated assemblies have proven to be an obstacle in itself. After getting the correct version of Castle.Windsor - it cannot find the correct section in the app.config file. The syntax in both Rhino Service Bus and the CastleBootstrapper has been changed as well - and I'm now totally confused. The 'documentation' on Hibernating Rhinos is really not helping me get started.
Could anyone please help me a working sample with Rhino Service Bus with either Castle Windsor v. 3.0 (beta) or 2.5.3, point me at something already online or just giving me a step-by-step pointers on what I need to get up and running?
after downloading the latest Rhino-ESB bits from github (https://github.com/hibernating-rhinos/rhino-esb) and building it, it's pretty straightforward to get started.
I have a asp.net MVC application which communicates with a backend through Rhino-ESB.
On the asp.net MVC side:
On global.asax.cs:
private IWindsorContainer _container;
protected void Application_Start()
{
_container = new WindsorContainer();
new RhinoServiceBusConfiguration().UseCastleWindsor(_container).Configure();
_container.Install(new YourCustomInstaller());
//Don't forget to start the bus
_container.Resolve<IStartableServiceBus>().Start();
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(_container));
}
Note that YourCustomInstaller must implement IWindsorInstaller and you register your controllers with the container in the Installmethod:
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.Register(Component
.For<HomeController>().LifeStyle.PerWebRequest.ImplementedBy<HomeController>());
Also note that the WindsorControllerFactory internally delegates controller creation to the container:
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return null;
return (IController)this.container.Resolve(controllerType);
}
Last but not least, provide the configuration on your web.config
<configSections>
<section name="rhino.esb" type="Rhino.ServiceBus.Config.BusConfigurationSection, Rhino.ServiceBus"/>
</configSections>
<rhino.esb>
<bus threadCount="1"
numberOfRetries="5"
endpoint="rhino.queues://localhost:31316/Client"
queueIsolationLevel="ReadCommitted"
name="Client"/>
<messages>
<add name="YourMessagesNamespace"endpoint="rhino.queues://localhost:31315/Backend"/>
</messages>
</rhino.esb>
This configuration assumes that the backend runs a queue in localhost:31315 and the client runs its queue on localhost:31316.
On the backend side:
assuming we're running it as a console application,
static void Main(string[] args)
{
IWindsorContainer container;
container = new WindsorContainer();
new RhinoServiceBusConfiguration()
.UseCastleWindsor(container)
.Configure();
var host = new RemoteAppDomainHost(typeof(YourBootstrapper));
host.Start();
Console.WriteLine("Starting to process messages");
Console.ReadLine();
Notice that YourBootstrapperclass implements CastleBootstrapper
public class YourBootstrapper: Rhino.ServiceBus.Castle.CastleBootStrapper
{
protected override void ConfigureContainer()
{
Container.Register(Component.For<OneOfYourMessages>());
}
}
in which we're registering a consumer for OneOfYourMessages