Lets say I have a main component that I want to initialize in a specific way and I have it's constructor take an Interface for this purpose. Is there a way to define the implementation I want for this interface in my xml and in turn inject that into the main component as a parameter? Like this:
public interface IComponent2 {
void DoStuff();
}
public class ConcreteCompImpl2 : IComponent2 {
IComponent1 _comp;
public ConcreteCompImpl2(IComponent1 comp) {
_comp = comp;
}
public void DoStuff(){
//do stuff
}
}
<component id="component1" service="ABC.IComponent1, ABC" type="ABC.ConcreteCompImpl1, ABC" />
<component id="component2" service="ABC.IComponent2, ABC" type="ABC.ConcreteCompImpl2, ABC" >
<parameters>
<component1>???</component1>
</parameters>
</component>
Or am I thinking about this all wrong and there is a much simpler way to accomplish this? The main thing I want to be able to do is configure what 'kind' of IComponent1 will get injected whenever an IComponent2 is created. Thanks
If you have only one concrete class implementing IComponent1, then it will automatically be injected when you resolve IComponent2.
If you have several classes implementing IComponent1 and want a specific one every time IComponent2 is resolved, you need to specific an inline dependency:
container.Register(
Component.For<IComponent2>()
.ImplementedBy<Component2>()
.DependsOn(Dependency.OnComponent<IComponent1, YourSpecialComponent1>())
);
I'm not completely sure you can specify this in the XML configuration, but honestly you should use the Fluent API instead of the XML configuration unless you have a really compelling reason to use it. As mentioned in the above link:
Ability to register components in XML is mostly a leftover from early days of Windsor before Fluent Registration API was created. It is much less powerful than registration in code and many tasks can be only accomplished from code.
Related
My problem is that I'm referencing a static resource, primary (as a color) in my MainPage, but when I have MainPage in the constructor of App() for Dependency Injection, primary isn't defined yet because its reference is loaded from App.xaml.
public partial class App : Application
{
//MainPage gets loaded first with a reference to
//StaticResource Primary which doesn't exist yet.
//An exception is thrown in mp.InitializeComponent()
//complaining about StaticResource Primary not existing
public App(MainPage mp, MainPageViewModel vm)
{
//StaticResource Primary is defined in here
InitializeComponent();
MainPage = mp;
MainPage.BindingContext= vm;
}
}
I can work around this by doing the following:
Adding Colors (and Styles) to the ResourceDictionary of MainPage:
<ContentPage.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ContentPage.Resources>
But then I also have to add a ResourceDictionary in Styles.xaml referencing Colors.xaml:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
This approach is fine for a small app, but I have many view pages utilizing Colors.xaml in the app I'm developing. Is there any way I can globally reference Colors.xaml and Styles.xaml in MauiProgram.cs? (So they're registered before DI takes place with MainPage)
You don't need to reference colors. What you actually ask is:
"How to resolve services with dependency injection".
You can add to any class IServiceProvider, and "ask" for a service to be provided to you. When you "ask" for your View, it will cascade (or to use your term: dominos down the line) and call the required constructors for ViewModel. And if your ViewModel uses something like ISettings, It will call the constructor of your MySettings class that implements the interface and so on.
The whole idea of using DI is to let it do this work for you.
You should not pass pages, in the constructor of you Application class. Why? Because when you construct it, LoadFromXaml is not called yet. But nothing is stopping you from having IServiceProvider in your constructor. And request services after Initialization is done.
This is good theory: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-7.0
This is good example: https://stackoverflow.com/a/32461714/6643940
In my controller, via service, I get from DB a list of the names of widgets (eg. chart, calendar, etc). Every widget implements WidgetInterface and may need other services as its own dependencies. The list of widgets can be different for each user, so I don't know which widgets / dependencies I will need in my controller. Generally, I put dependencies via DI, using factories, but in this case I don't know dependencies at the time of controller initialization.
I want to avoid using service locator directly in controller. How can I manage that issue? Should I get a list of the names of widgets in controller factory? And depending on widgets list get all dependencies and put them to controller?
Thanks, Tom
Solution
I solved my issue in a way that suggested Kwido and Sven Buis, it means, I built my own Plugin Manager.
Advantages: I do not need use service locator directly in controller and I have clear and extensible way to get different kinds of widgets.
Thank you.
Create your own Manager, like some sort of ServiceManager, for your widgets.
class WidgetManager extends AbstractPluginManager
Take a look at: Samsonik tutorial - pluginManager. So this way you can inject the WidgetManager and only retrieve the widgets from this manager as your function: validatePlugin, checks whether or not the fetched instance is using the WidgetInterface. Keep in mind that you can still call the parent ServiceManager.
Or keep it simple and build a plugin for your controller that maps your widget names to the service. This plugin can then use the serviceLocator/Manager to retrieve your widget(s), whether they're created by factories or invokableFactories. So you dont inject all the widget directly but only fetch them when they're requested. Something realy simplistic:
protected $map = [
// Widget name within the plugin => Name or class to call from the serviceManager
'Charts' => Widget\Charts::class,
];
public function load($name)
{
if (array_key_exists($name, $this->map)) {
return $this->getServiceManager()->get($this->map[$name]);
}
return null;
}
Injecting all the Widgets might be bad for your performance so you might consider something else, as when the list of your widgets grow so will the time to handle your request.
Hope this helped you and pushed you in some direction.
This indeed is a interesting question. You could consider using Plugins for the widgets, which can be loaded on the fly.
Depency injection is a good practise, but sometimes, with dynamic content, impossible to implement.
Another way to do this, is to make your own widget-manager. This manager then can load the specific widgets you need. The widget-manager can be injected into the controller.
Edit:
As you can see above, same idea from #kwido.
I would use a separate service and inject that into the controller.
interface UserWidgetServiceInterface
{
public function __construct(array $widgets);
public function getWidget($name);
}
The controller factory
class MyControllerFactory
{
public function __invoke(ControllerManager $controllerManager, $name, $requestedName)
{
$serviceLocator = $controllerManager->getServiceLocator();
$userWidgetService = $serviceLocator->get('UserWidgetService');
return new MyController($userWidgetService);
}
}
Then the logic to load the widgets would be moved to the UserWidgetServiceFactory.
public function UserWidgetServiceFactory
{
public function __invoke(ServiceManager $serviceLocator, $name, $requestedName)
{
$userId = 123; // Load from somewhere e.g session, auth service.
$widgetNames = $this->getWidgetNames($serviceLocator, $userId);
$widgets = $this->loadWidgets($serviceManager, $widgetNames);
return new UserWidgetService($widgets);
}
public function getWidgetNames(ServiceManager $sm, $userId)
{
return ['foo','bar'];
}
public function loadWidgets(serviceManager $sm, array $widgets)
{
$w = [];
foreach($widgets as $widgetName) {
$w[$widgetName] = $sm->get($widgetName);
}
return $w;
}
}
The call to loadWidgets() would eager load all the widgets; should you wish to optimise this you could register your widgets as LazyServices
I have following;
interface IRepository
--interface IRepositoryEF: IRepository
--interface IRepositoryNH: IRepository
----interface ICategoryRepositoryEF: IRepositoryEF
----interface ICategoryRepositoryNH: IRepositoryNH
I want to use CategoryRepositoryEF and CategoryRepositoryNH classes in the service. How can I inject them into CategoryService?
CategoryService(IRepository repository)
{
}
What is the best practice about this? Could I use a RepositoryFactory and inject it into service and then create repositories in the services?
I mean something like following;
CategoryService(CategoryRepositoryFactory factory)
{
var CategoryRepositoryEF = factory.Create("EF");
var CategoryRepositoryNH = factory.Create("NH");
}
Is this good idea? Or I m completely wrong?
The idea is a bit off.
the purpose of repository interfaces is to abstract away the data source. the goal is to allow all using classes to fetch information without knowing where the data comes from. But in your case you force the classes to know wether NHibernate or EntityFramework is used. Why?
I also wouldn't have a IRepository interface, as you create specific interfaces.
Now the question is rather "How do I map a nhibernate or entity framework repository to one of my interfaces".
That should be done when the application starts. You typically do something like this:
container.Register<ICategoryRepostitory, NHCategoryRepository>();
container.Register<IUserRepostitory, EFUserRepository>();
If you don't do that you have effectivly coupled the using code with a specific implementation. There is really no need for any interfaces at all then.
Update
Which repository is used if I inject like CategoryService(ICategoryRepository repository)?
The repository that you registered in your inversion of control container. My point is that you should not register both implementations but just one of them for every repository.
A simple example would be below.
First manage the repository type with your web.config:
<configuration>
<appSettings>
<add key="RepositoryType" value="NHibernate" />
</appSettings>
</configuration>
And then do this when you configure your inversion of control container:
if (ConfigurationManager.AppSettings["RepositoryType"] == "NHibernate"))
{
_autofac.RegisterType<NHCategoryRepository>.As<ICategoryRepository>();
_autofac.RegisterType<NHUserRepository>.As<IUserRepository>();
}
else
{
_autofac.RegisterType<EFCategoryRepository>.As<ICategoryRepository>();
_autofac.RegisterType<EFUserRepository>.As<IUserRepository>();
}
You could use a dependency injector as Ninject and inject parameter trough contructor
Your NinjectModule
public class NinjectModule : NinjectModule
{
public override void Load()
{
this.Bind<ICategoryRepositoryEF>().To<CategoryRepositoryEF>();
this.Bind<ICategoryRepositoryNH >().To<CategoryRepositoryNH>();
this.Bind<ICategoryService >().To<CategoryService>();
}
}
Inject dependencies
var kernel = new StandardKernel(new NinjectModule());
var categoryService = kernel.Get<ICategoryService>();
This is just an example but take into account that dependencies should be injected on the composition root (entry point) of the application.
Simple question... quite possibly not simple answers :)
I want to write an accessibility service for android in monodroid. I'm finding it difficult to work out the pieces. I can inherit from AccessibilityService, but there isn't an AccessibilityServiceAttribute. I need specific content in the manifest file, but apparently the intent filter and service xml is not directly supported by monodroid... I believe.
I doubt this is something that many people have done, but as a beginner with Monodroid, I think I'm mainly just finding it difficult to put together the well supported "normal" app development, with the "supported" but not quite, accessibility service development.
I would appreciate any pointers. Thanks guys.
According to the docs:
An accessibility is declared as any other service in an AndroidManifest.xml but it must also specify that it handles the "android.accessibilityservice.AccessibilityService" Intent. Failure to declare this intent will cause the system to ignore the accessibility service.
Since it is declared the same way as any other service, you can make use of ServiceAttribute and IntentFilterAttribute to generate the entry in AndroidManifest.xml. For example:
[Service]
[IntentFilter(new[] { "android.accessibilityservice.AccessibilityService" })]
public class MyAccessibilityService : AccessibilityService
{
public override void OnAccessibilityEvent(AccessibilityEvent e)
{
}
public override void OnInterrupt()
{
}
}
When you build the application, Mono for Android will generate this in the manifest:
<service android:name="sample.MyAccessibilityService">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
</service>
High Level
With StructureMap, Can I define a assembly scan rule that for an interface IRequestService<T> will return the object named TRequestService
Examples:
FooRequestService is injected when IRequestService<FooRequest> is requested
BarRequestService is injected when IRequestService<BarRequest> is requested
Details
I have a generic interface defined
public interface IRequestService<T> where T : Request
{
Response TransformRequest(T request, User current);
}
and then I have multiple Request objects that implement this interface
public class FooRequestService : IRequestService<Foo>
{
public Response TransformRequest(Foo request, User current) { ... }
}
public class BarRequestService : IRequestService<Bar>
{
public Response TransformRequest(Bar request, User current) { ... }
}
Now I am at the point where I need to register these classes so that StructureMap knows how to create them because in my controller I want have the following ctor (which I want StructureMap to inject a FooRequestService into)
public MyController(IRequestService<Foo> fooRequestService) { ... }
Right now to get around my issue I have implemented an empty interface and instead of having the FooRequestService implement the generic interface I have it implement this empty interface
public interface IFooRequestService : IRequestService<Foo> { }
Then my controllers ctor looks like so, which works with StructureMaps' Default Convention Scanner
public MyController(IFooRequestService fooRequestService) { ... }
How could I create a rule with StructureMap's assembly scanner to register all objects named TRequestService with IRequestService<T> (where T = "Foo", "Bar", etc) so that I don't have to create these empty Interface definitions?
To throw something else into the mix, where I am handling StructureMap's assembly scanning does not have any reference to the assembly that defines IRequestService<T> so this has to use some sort of reflection when doing this. I scanned the answer to "StructureMap Auto registration for generic types using Scan" but it seems as though that answer requires a reference to the assembly that contains the interface definition.
I am on the path of trying to write a custom StructureMap.Graph.ITypeScanner but I am kind of stuck on what to do there (mainly because I have little experience with reflection).
You are on the right path with the scanner. Thankfully there is one built into StructureMap. Unfortunately it is not yet, as of this writing, released. Get the latest from trunk and you will see a few new things available within the scanner configuration. An example for your needs is below.
public class MyRegistry : Registry
{
public MyRegistry()
{
Scan(x =>
{
x.TheCallingAssembly();
//x.AssembliesFromApplicationBaseDirectory();
x.WithDefaultConventions();
x.ConnectImplementationsToTypesClosing(typeof (IRequestService<>));
});
}
}
First you need to tell the scanner configuration which assemblies to include in the scan. The commented AssembliesFromApplicationBaseDirectory() method also might help if you are not doing a registry per assembly.
To get your generic types into the container use ConnectImplementationsToTypesClosing.
For an example on how to setup use registries when setting up the container see:
http://structuremap.sourceforge.net/ConfiguringStructureMap.htm
If you like you can skip using registries in general and just do a scan within ObjectFactory.Initialize.
Hope this helps.