Been googling it for a while, but found nothing really good.
So, the thing is, in MVVM Cross guides our solution (for mobile platforms) is separated on, as example, HelloWorld.Core project and few platform specific projects, as example HelloWorld.Android.UI.
You can see full tutorial here https://www.mvvmcross.com/documentation/tipcalc-tutorial/the-tip-calc-tutorial
And, the thing is, in our .Core project we creating Services, ViewModels and we registering it in MVVM Cross provided IoC-container.
In example which provided by the MvvmCross documentation Service implementation is really simple, just one function like that
public class CalculationService: ICalculationService
{
public double TipAmount(double subTotal, int generosity)
{
return subTotal * ((double)generosity)/100.0;
}
}
so we just go ahead, registering it in our App.cs and then its fine to use in binded ViewModel
public class App: MvxApplication
{
public App()
{
Mvx.RegisterType<ICalculationService, CalculationService>();
Mvx.RegisterSingleton<IMvxAppStart>(new MvxAppStart<HomeViewModel>());
}
}
View model code :
public class TipViewModel : MvxViewModel
{
readonly ICalculation _calculation;
public TipViewModel(ICalculation calculation)
{
_calculation = calculation;
}
// ...
double _tip;
public double Tip
{
get {
return _tip;
}
set
{
_tip = value;
RaisePropertyChanged(() => Tip);
}
}
void Recalculate()
{
Tip = _calculation.TipAmount(SubTotal, Generosity);
}
}
But the thing is, when I started playing around with that, I decided, to, let say, code some simple local storage data scanner to find some specific files.
Simple code example:
public class SimpleDataScanner : IBaseDataScanner
{
private string _basePath { get; set; }
private List<string> _scanResult { get; set; }
public SimpleDataScanner()
{
_basePath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
_scanResult = new List<string>();
}
public List<string> BasicDataScan(string startPath = null)
{
string startFolder = _basePath;
if (startPath.Length > 0 && startPath.Length != null)
{
startFolder = Path.Combine(_basePath, startPath);
}
try
{
TreeScan(startFolder);
}
catch (Exception)
{
throw;
}
return _scanResult;
}
private void TreeScan(string path)
{
foreach (var file in Directory.GetFiles(path))
{
if (Path.HasExtension("pdf"))
{
_scanResult.Add(file);
}
}
foreach (string dir in Directory.GetDirectories(path))
{
TreeScan(dir);
}
}
}
And, as I found out, the problems are:
1) I can't use Android specific functions and properties in PCL
2) I can't use reference in my PCL Core project to, lets say, some other Android class lib project
So, I can't create working Service which means I can't register it in IoC-container and pass to ViewModel.
Whats the way of doing such things?
Any tutorials? Or I getting whole idea wrong?
You need to implement the interface in your platform code, and register the concrete implementation against the interface in Setup.cs inside your platform project. So in your case, IDataScanner would exist inside your Core project, while SimpleDataScanner would live in the Android project.
Inside Setup.cs, you'd register the concrete implementation like this:
protected override void InitializeFirstChance()
{
base.InitializeFirstChance();
Mvx.RegisterSingleton<IDataScanner>(() => new SimpleDataScanner());
}
Essentially, if the interface requires platform-specific or full .NET framework code to implement, those per-platform implementations live in the platforms and are registered in the platform setup. If both the interface and the implementation are shared within the Core project, the dependency can be registered in App.cs.
Related
I have a .NET project with 50+ WebAPI Controllers. Dependencies injected in constructor:
// One of this APIs
public class ProductAController : ApiController
{
private readonly IProductDataProvider _productDataProvider;
// Constructor usually requires a lot of dependencies (for user sesisons, calculations, crud operations, some external integrations etc)
public ProductAController(IProductDataProvider productDataProvider)
{
this._productDataProvider = productDataProvider;
}
}
public interface IProductDataProvider
{
public bool ProductExists(string productName);
}
public class ProductDataProvider
{
private readonly IDbConnectionProvider _dbConnectionProvider;
public ProductDataProvider(IDbConnectionProvider dbConnectionProvider)
{
this._dbConnectionProvider = dbConnectionProvider;
}
public bool ProductExists(string productName)
{
//...
}
}
public interface IDbConnectionProvider
{
public string GetConnectionString();
}
public class DbConnectionProvider
{
// This is the config that I need to set up for several APIs
private readonly ModuleConfig _moduleConfig;
public DbConnectionProvider(ModuleConfig moduleConfig)
{
this._moduleConfig = moduleConfig;
}
public string GetConnectionString();
{
//...
}
}
IDbConnectionProvider, ModuleConfig, and IProductDataProvider registered in Autofac (all 50 API use single ModuleConfig):
builder.RegisterInstance(pdkProvidersSettings).AsSelf().SingleInstance();
builder.RegisterType<DbConnectionProvider>().As<IDbConnectionProvider>().InstancePerDependency();
builder.RegisterType<ProductDataProvider>().As<IProductDataProvider>().InstancePerDependency();
Now I need to use different instances of ModuleConfig for some APIs.
I'll have to make IConfigProvider.GetModuleConfig(string key, string ownerKey) (and call it with "ModuleConfig" and "productA123" params)
I was thinking about Named and Keyed Services feature or IIndex in Autofac, I also tried to pass moduleConfig across all that chain (but it required too much changes).
It seems like I have a design problem here but I can't figure it out.
The expected result is something like that:
API gets a request containing the key ("productA1" or "productA2". Thats why I can't hardcode "productA" in metadata attribute of ProductAController)
When it comes to IDbConnectionProvider it gets specific ModuleConfig and is able to get specific connection string
The result is that different API modules can use different DBs (that is the main requirement)
I am creating sample project based on DDD.
I created SharedKernel project where I have my class for DomainEvents
public static class DomainEvents
{
public static IContainer Container { get; set; }
static DomainEvents()
{
Container = StructureMap.Container.For<GenericTypesScanning>();
}
public static void Raise<T>(T args) where T : IDomainEvent
{
foreach (var handler in Container.GetAllInstances<IHandle<T>>())
{
handler.Handle(args);
}
}
}
and this is class GenericTypesScanning
public class GenericTypesScanning : Registry
{
public GenericTypesScanning()
{
Scan(scan =>
{
// 1. Declare which assemblies to scan
scan.Assembly("MyLib");
// 2. Built in registration conventions
scan.AddAllTypesOf(typeof(IHandle<>));
scan.WithDefaultConventions();
});
}
}
In MyLib project I have class AppointmentConfirmedEvent and handler for this event:
public class EmailConfirmationHandler: IHandle<AppointmentConfirmedEvent>
{
public void Handle(AppointmentConfirmedEvent appointmentConfirmedEvent)
{
// TBD
}
}
I have temporary rest api controller where I wanted to check if everything is correctly registered and I am doing this:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET: api/<ValuesController>
[HttpGet]
public IEnumerable<string> Get()
{
var appointmentConfirmedEvent = new AppointmentConfirmedEvent();
DomainEvents.Raise(appointmentConfirmedEvent);
return new string[] { "value1", "value2" };
}
}
but when DomainEvents.Raise is called the event is not handled because internal call Container.GetAllInstances<IHandle<T>>() returns empty array.
I did analogous example with Console app and there everything works fine. Any idea why it does not work in case of ASP.NET Core .NET 5 project?
-Jacek
The AddAllTypesOf() method does not work with open generics. See the ConnectImplementationsToTypesClosing() method in the StructureMap docs: http://structuremap.github.io/generics/
And just a reminder, StructureMap is no longer supported. Moreover, 2.6.4.1 was the "haunted" version of StructureMap that was admittedly buggy.
The first thing to do is to check out the type scanning diagnostics:
http://structuremap.github.io/diagnostics/type-scanning/.
The type scanning can be a little brittle if there are missing assemblies. The diagnostics might point out where things are going wrong. Also, try your WhatDoIHave() diagnostics too.
And also, just making sure that you know that StructureMap is no longer supported and has been replaced by Lamar:
https://jeremydmiller.com/2018/01/29/sunsetting-structuremap/
https://jasperfx.github.io/lamar
I'm just taking my first baby steps in the MEF territory and wanted to do so using .net core 2.1.
Using VS 2017 (version 15.8.8) I've done a small Console App (.NET Core) with an interface
interface IMessageSender
{
void Send(string message);
}
and an implementation (in the same project)
[Export(typeof(IMessageSender))]
public class EmailSender : IMessageSender
{
public void Send(string message)
{
Console.WriteLine("EmailSender : " + message);
}
}
Finally I have a small compose method executed from my Main(string[] args)
[Import]
private void Compose()
{
var assembly_A = new[] { typeof(Program).GetTypeInfo().Assembly };
var config_A = new ContainerConfiguration().WithAssembly(assembly_A[0]);
var container_A = config_A.CreateContainer();
var msg_A = container_A.GetExport<IMessageSender>();
msg_A.Send("Hello");
}
It works as expected
However, if I add a new class library to my solution and move my implementation of Send(string) to the newly added project things do not work out.
namespace AnotherMefExtensionProjectNamespace
{
[Export(typeof(IMessageSender))]
public class EmailSenderExtended : IMessageSender
{
public void Send(string message)
{
Console.WriteLine("EmailSenderExtended : " + message);
}
}
}
The new Compose method
[Import]
public IMessageSender MessageSender { get; set; }
private void Compose()
{
var assembly_B = new[] { typeof(EmailSenderExtended).GetTypeInfo().Assembly };
var config_B = new ContainerConfiguration().WithAssembly(assembly_B[0]);
var container_B = config_B.CreateContainer();
var msg_B = container_B.GetExport<IMessageSender>();
msg_B.Send("Hello");
}
I've tried to compare the different configs and containers (_A versus _B in the examples) but can't understand what is different. I've even tried to extend the class ContainerConfiguration to load from a specified assembly and it works as long as the given file contains the Main method but fails if I use my "extended" .NET Core Class Library.
public static ContainerConfiguration WithChosenAssembly(this ContainerConfiguration configuration, string pathAndFile)
{
var context = AssemblyLoadContext.Default.LoadFromAssemblyPath(pathAndFile);
var ass_list = new List<Assembly>() { context };
configuration = configuration.WithAssemblies(ass_list, null);
return configuration;
}
I was under the impression that you extend your main application by developing a class library that basically implements the interfaces specified.
I seem to be unable to do this currently, but obviously I misunderstood something very basic.
If someone would care to put me on the right track or give me an alternative idea for "plug-in" development for .net core I would be very grateful.
King regards
Magnus
I realized that my test setup does not mimic any real world scenario and thus I brought my problems on myself.
Obviously I should have had three projects.
One project with only the interface definitions.
One "main" project where all my regular code exists.
One (or more) projects where my MEF implementations of the interfaces exist.
Reviewing my example and adhering to the obvious "design" above it all works exactly as it should.
Most StackOverflow users probably wouldn't make my blunder but for those that did, I hope the above helps. :-)
I have a Portable class library which I am going to use for Windows 8 and phone apps. So I created a common viewmodeland, and now I want to handle storage functionality in viewmodels. I found a PCLStorage package which is used to deal with this scenario.
I have a code like this on Windows 8 and now I want to write this in PCL using PCLStorage.
public static object LoadSettings(string key)
{
if (Windows.Storage.ApplicationData.Current.LocalSettings.Values.ContainsKey(key))
{
if (Windows.Storage.ApplicationData.Current.LocalSettings.Values[key] != null)
{
return Windows.Storage.ApplicationData.Current.LocalSettings.Values[key];
}
else
{
return null;
}
}
else
{
return null;
}
}
I found a way, I have created a interface for Storage used in viewmodel. And have created a class to handle storage for phone and metro in respective projects. When I am creating a object of MainViewModel I am passing storage object also.
So I want to know, is this right?
I believe the simplest pattern would be to Register the current-platform-defendant-implementation within the container bootstrapper on the 'specific' 'shell' for platform and get the dependenciy in the constructor
Or simply skip the IoC thing and instantiate stuff manually
using MyInfrastructureAssembly.Interfaces;
public MyApp: App // MyNewPlatFormApp
{
public override Initiaze()
{
var bootsrapper = new Boostrapper(MyIoC.Current);
}
}
public class Bootsrapper: TheBootStrapperOfMyIoc
{
public Bootsrapper(IocContainer container)
{
Container = container;
}
public override Register()
{
Container.Register<IMyAbstractedService,MyPlatformDependantService>();
}
}
MyPlatformDependantService : IMyAbstractedService
{
public object Get(); // IMyAbstractedService.Get()
}
public class MyViewModel:ViewModelBase
{
IMyService MyService {get;set;}
MyViewModel(IMyAbstractedService myServcice)
{
MyService = myService;
}
public object Thing // LazyThing provided by IMyAbstractedService
{
get
{
if(_thing!=null)
return _thing;
return _thing = GetIt();
}
set
{
if(Equals(value,_thing)) return;
_thing = value;
base.NotifyMagicalChanges()
}
}
public void GetIt()
{
MyServcie.Get();
}
}
Static Version : Runs on LinqPad
enter code here
private async void CheckIfExist()
{
try
{
var isoStore = FileSystem.Current.LocalStorage;
var folder = await isoStore.CreateFolderAsync("xxx",
CreationCollisionOption.ReplaceExisting);
ExistenceCheckResult result = await isoStore.CheckExistsAsync("xxx");
switch (result)
{
case (ExistenceCheckResult.FolderExists):
Console.WriteLine(":)"); break;
case (ExistenceCheckResult.NotFound):
Console.WriteLine(":("); break;
}
}
catch (Exception)
{
Console.WriteLine(":<");
}
}'
I found a way, I have created a interface for Storage used in viewmodel. And have created a class to handle storage for phone and metro in respective projects. When I am creating a object of MainViewModel I am passing storage object also. So i want to know that Is this right?
Yes, this is a good solution. Dan's answer is basically just the same thing but using an IoC container.
You can also try the Xam.Plugins.Settings library, which basically does for settings what PCL Storage does for File IO.
I've just switched to using the NInject.MockingKernel extension for my tests (NSubstitute).
However, it makes very hard to run my Web API integration tests because it will return mocks for all Web API interfaces also.
Can I automatically limit its application only to namespaces of my own?
I don't see how that is possible out of the box. Although it's not very hard to create such a kernel on your own.
This sample is of course very minimalistic though, but it should show you how it could be done. Or maybe there is someone with more knowledge of the Ninject internals.
public class NamespaceFilteringMockMissingBindingsResolver : MockMissingBindingResolver
{
public NamespaceFilteringMockMissingBindingsResolver(IMockProviderCallbackProvider mockProviderCallbackProvider)
: base(mockProviderCallbackProvider)
{
}
protected override bool TypeIsInterfaceOrAbstract(Type service)
{
return base.TypeIsInterfaceOrAbstract(service) && service.Namespace != null && service.Namespace.StartsWith("YourNamespace");
}
}
public class CustomNSubstituteMockingKernel : NSubstituteMockingKernel
{
public CustomNSubstituteMockingKernel()
{
this.AddComponents();
}
public CustomNSubstituteMockingKernel(INinjectSettings settings, params INinjectModule[] modules)
: base(settings, modules)
{
this.AddComponents();
}
private new void AddComponents()
{
this.Components.RemoveAll<IMissingBindingResolver>();
this.Components.Add<IMissingBindingResolver, SingletonSelfBindingResolver>();
this.Components.Add<IMissingBindingResolver, NamespaceFilteringMockMissingBindingsResolver>();
}
}
Update
You are right, you don't need to create your own kernel. You can also do it like this.
var kernel = new NSubstituteMockingKernel();
kernel.Components.RemoveAll<IMissingBindingResolver>();
kernel.Components.Add<IMissingBindingResolver, SingletonSelfBindingResolver>();
kernel.Components.Add<IMissingBindingResolver, NamespaceFilteringMockMissingBindingsResolver>();
Creating your own kernel might just be more handy than always writing those extra lines. Or you create some kind of factory method.