In Xamarin.Forms I have a simple container (of AutoFac, but it can be any other container) with only 2 mappings:
static class BootStrap
{
public static IContainer Configure()
{
var builder = new ContainerBuilder();
builder.RegisterType<MainPageViewModel>().As<IMainPageViewModel>().SingleInstance();
builder.RegisterType<MainPage>().As<IMainPage>().SingleInstance();
return builder.Build();
}
}
This is the MainPage class, which includes a ctor, with an IMainPageViewModel parameter:
public partial class MainPage : ContentPage, IMainPage
{
private IMainPageViewModel _mainPageViewModel;
public MainPage(IMainPageViewModel mainPageViewModel)
{
InitializeComponent();
_mainPageViewModel = mainPageViewModel;
}
}
This is the MinPageViewModel class:
public class MainPageViewModel : INotifyPropertyChanged, IMainPageViewModel
{
}
In order to establish the mappings I use this App class
public App()
{
InitializeComponent();
MainPage = new NiceMasterDetailPage();
BootStrap.Configure();
}
The error I get is that I don't have a default ctor for MainPage, but I don't want to use the default ctor for MainPage.
I want to use a ctor which has an interface as its parameter - this is how Dependency Injection should work. Does someone have an idea how to overcome this problem?
Resolve the desired page via the container instead of trying to initialize it manually
//...
IContainer container;
public App() {
InitializeComponent();
container = BootStrap.Configure();
MainPage = container.Resolve<MainPage>();
}
//...
That way the container will inject any dependencies of the type being resolved
Related
I am new to .NET Core and I got stuck on this.
In my startup.cs, I have
services.AddScoped<IEmailService, EmailService>();
and I have created a IHostedService:
services.AddHostedService<CooldownExpiredService>();
inside my CooldownExpiredService, I have the following code
using (var emailScope = _serviceScopeFactory.CreateScope())
{
var _emailService = emailScope.ServiceProvider.GetService<EmailService>();
}
but the object I get is null. How can I get the EmailService inside from HostedService?
i dont knwo why i should have use the interface.
emailScope.ServiceProvider.GetService<IEmailService>()
You can add Extension Method for all Services
first : add class =>
public static class ServiceTool
{
private static IServiceProvider ServiceProvider { get; set; }
public static IServiceCollection Create(IServiceCollection servicesProvider)
{
ServiceProvider = servicesProvider.BuildServiceProvider();
return servicesProvider;
}
public static T Resolve<T>()
{
return ServiceProvider.GetService<T>();
}
}
And go to the Startup and add it
ServiceTool.Create(services);
For use in controller write on constructor controller
example:
_logRepository = ServiceTool.Resolve<ILogRepository>();
i want to create data to all the application
using static System.Net.Mime.MediaTypeNames;
public class Session
{
void btnLoginClick()
{
NombreUsuario = Username.Text;
Application.Current.Properties["name"] = NombreUsuario;
Application.Current.Properties["IsLoggedIn"] = true;
}
}
i have this error
'MediaTypeNames.Application' does not contain a definition for 'Current'
My app:
namespace Dinamic.Trailer.Mobile.Forms
{
public partial class App : Application
{
public App()
{
Locator.ConfigureIoC();
InitializeComponent();
}
protected override async void OnStart()
{
await InitNavigation();
}
//onsleep()//OnStart
private Task InitNavigation()
{
var navigationService = Content.Resolve<MainNavigation>();
return navigationService.InitializeAsync();
}
}
}
Remove this line
using static System.Net.Mime.MediaTypeNames;
And use import the using Xamarin.Forms;
After that you can use Application.Current.Properties["key"] = value;
Use using xamarin.forms namespace while you use Application.Current.Properties for storing data. Either you could use App.Current.Properties if you do not want to add xamarin.forms namespace in your cs file.
Here App refer your App class in App.xaml.cs which inherit from Xamarin.forms.Application. so you can use App.Current.Properties instead Application.Current.Properties.
Thank you.
I have a problem with the Automapper on my website and I can't find a solution.
I've created a class called AutoMapperProfile where I'd like to put all my Maps
public class AutoMapperProfile: Profile
{
private readonly IConfiguration _mapper;
public AutoMapperProfile(IConfiguration mapper)
{
_mapper = mapper;
}
protected override void Configure()
{
base.Configure();
_mapper.CreateMap<SlideDTO, Slide>();
_mapper.CreateMap<Slide, SlideDTO>();
}
}
For DI purposes I'm using Ninject, so I've added the following bindings in NinjectWebCommon:
kernel.Bind<IMappingEngine>().ToMethod(ctx => Mapper.Engine);
kernel.Bind<IConfigurationProvider>().ToMethod(x => Mapper.Engine.ConfigurationProvider);
The controller looks like this:
private readonly ISlideRepository slideRepository;
private readonly IMappingEngine mappingEngine;
public HomeController(
ISlideRepository slideRepository,
IMappingEngine mappingEngine)
{
this.slideRepository = slideRepository;
this.mappingEngine = mappingEngine;
}
[HttpGet]
public ActionResult Index()
{
var model = new IndexViewModel();
var slide = slideRepository.GetSlide();
model.Slide = mappingEngine.Map<SlideDTO, Slide>(slide);
return View(model);
}
When I map from SlideDTO to Slide I get the following error:
Missing type map configuration or unsupported mapping.
So my best guess is that I didn't do the binds correctly so that Automapper can see my maps, but I'm not sure how can I fix it.
You don't need to inject IConfiguration into AutoMapperProfile, it already inherits a CreateMap method from Profile.
Make sure that AutoMapperProfile has a parameterless constructor like this:
public class AutoMapperProfile : Profile
{
protected override void Configure()
{
this.CreateMap<SlideDTO, Slide>();
this.CreateMap<Slide, SlideDTO>();
}
}
And then you need to make sure that AutoMapper knows about this profile, here is how you can do it:
Mapper.Engine.ConfigurationProvider.AddProfile<AutoMapperProfile>();
Please note that you can invoke the AddProfile method on any IConfigurationProvider (if you decide not to use the global ConfigurationProvider and Engine).
I am beginning using MVVMLight with Xamarin.Forms and need some help to get the IOC set up to create my first ContentPage in App.GetMainPage.
My ViewModels have constructors like so -
public class NewsType1ViewModel : ViewModelBase
{
private readonly IContentService contentService;
private readonly INavigation navigation;
public List<ContentModel> ContentModels { get; set; }
public NewsType1ViewModel (INavigation navigation, IContentService contentService)
{
this.contentService = contentService;
this.navigation = navigation;
}
My ContentPages have constructors like so -
public partial class NewsType1CP : ContentPage
{
private NewsType1ViewModel vm;
public NewsType1CP (NewsType1ViewModel vm)
{
InitializeComponent ();
I am using a ViewModelLocator class as follows -
public class ViewModelLocator
{
public ViewModelLocator ()
{
ServiceLocator.SetLocatorProvider (() => SimpleIoc.Default);
// Services
if (App.StubMode) {
SimpleIoc.Default.Register<IContentService, ContentServiceStub> ();
} else {
SimpleIoc.Default.Register<IContentService, ContentService> ();
}
// How do I wire up INavigation?
// I could not just register default NavigationPage() as it has 2
// possible constructors so tried [PreferredConstructor] on my derived class
SimpleIoc.Default.Register<INavigation, AppNavigationPage> ();
// View Models
SimpleIoc.Default.Register<NewsType1ViewModel> ();
SimpleIoc.Default.Register<NewsDetailsViewModel> ();
}
public NewsType1ViewModel NewsType1ViewModel {
get {
return ServiceLocator.Current.GetInstance<NewsType1ViewModel> ();
}
}
public NewsDetailsViewModel NewsDetailsViewModel {
get {
return ServiceLocator.Current.GetInstance<NewsDetailsViewModel> ();
}
}
public static void Cleanup ()
{
// TODO Clear the ViewModels
}
}
public class AppNavigationPage : NavigationPage
{
[PreferredConstructor]
public AppNavigationPage ()
{
}
}
My App.cs is "in progress" as follows -
public static class App
{
public static AppNavigationPage Nav;
public static ViewModelLocator Locator = new ViewModelLocator ();
public static bool StubMode = true;
public static Page GetMainPage ()
{
try {
// Want to return a Nav with NewsType1CP as the starting Page
NewsType1CP newsType1CP = new NewsType1CP(ServiceLocator.Current.GetInstance<NewsType1ViewModel> ());
Nav.PushAsync (newsType1CP);
return Nav;
} catch (Exception ex) {
//
Exception baseexception = ex.GetBaseException ();
Debug.WriteLine (baseexception.Message);
}
return null;
}
}
My latest exception is -
Cannot cast from source type to destination type.
Am I barking up the wrong tree trying to supply an INavigation to each of my ViewModels like so?
Update: After a couple of answers showing other ways of controlling the Navigation in Xamarin Forms, I think it would help if someone could clarify why attempting constructor injection for Navigation is not such a good thing.
I think my example is a bit confusing with AppNavigationPage as a static, ideally I would like this to be in the IOC also, so I could just call return ServiceLocator.Current.GetInstance< AppNavigationPage >(), but I had a go with various factory methods and am in the middle of debugging this, so the code is obviously half baked ...
If you want a ready to use solution, instead of MVVM Light, you could try to use Xamarin Forms Labs ViewModel base, it injects a Navigation propety in your ViewModel:
that way you cold do something like this:
public Command NavigateToViewModel
{
get
{
return navigateToViewModel ?? (navigateToViewModel = new Command(
async () => await Navigation.PushAsync<NewPageViewModel>(),
() => true));
}
}
I'm not using MvvmLight, but I can tell you that yes, you're barking up the wrong tree in trying to supply an INavigation to each ViewModel.
The easiest way to achieve what you're trying to do is to use a public static on your App.
public static INavigation Navigation
{
get;
set;
}
public static Page GetMainPage()
{
var firstPage = new NavigationPage(new MyRootPage())
{
Navigation = firstPage.Navigation;
return firstPage;
}
}
Now this falls apart when you're using a MasterDetail page because you need your Navigation to wrap your DetailPage, not your MasterDetailPage. Therefore, don't set Navigation in the GetMainPage method, but instead from within the MD Page.
var master = new MainMenuPage();
var detail = new NavigationPage(new FirstPage());
if (App.Navigation == null)
{
App.Navigation = detail.Navigation;
}
Master = master;
Detail = detail;
Oh ok, when looking better at your code i maybe spotted the problem:
SimpleIoc.Default.Register<INavigation, AppNavigationPage> ();
Shoult it be:
SimpleIoc.Default.Register<INavigation, AppNavigationPage.Navigation> ();
I'm trying to add logging with aspect orientated programming using castle windsor in plain asp.net, i.e. not MVC
I've added a class that implements the IInterceptor interface and an attribute that inherits from Attribute.
public class LogAttribute : Attribute
{
public Level LogLevel { get; set; }
public LogAttribute(Level level)
{
LogLevel = level;
}
}
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
MethodInfo mi = invocation.Method;
LogAttribute[] atts = (LogAttribute[])mi.GetCustomAttributes(typeof(LogAttribute), true);
// if method not marked with InternalUseRestricted attribute, then pass on call
if (atts.Length == 0)
{
invocation.Proceed();
}
else
{
ISeiLogger log = LoggerFactory.GetLogger(mi.DeclaringType.ToString());
//assume only one logging attribute
//log on entry
log.LogEnter(atts[0].LogLevel);
//allow code to continue
invocation.Proceed();
//log on exit
log.LogExit(atts[0].LogLevel);
}
}
}
Now in the global.asax.cs I've added the following:
public partial class Global : System.Web.HttpApplication, IoCProvider
{
private void InitializeIoC()
{
container = new WindsorContainer();
container.Install(new Sei.Aspect.AspectInstaller());
}
public IWindsorContainer Container
{
get { return container; }
}
private static Sei.Logging.ISeiLogger log;
private IWindsorContainer container;
public override void Init()
{
base.Init();
InitializeIoC();
}
and I've created an installer class:
public class AspectInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
//container.Register(AllTypes.FromAssembly(Assembly.GetExecutingAssembly()).BasedOn<IInterceptor>().Configure(component => component.LifeStyle.PerWebRequest));
container.Register(Component.For<IInterceptor>().ImplementedBy<LoggingInterceptor>().LifeStyle.PerWebRequest);
container.Register(Component.For<IInterceptor>().ImplementedBy<InternalUseRestrictedInterceptor>().LifeStyle.PerWebRequest);
container.Register(Component.For<IInterceptor>().ImplementedBy<CachingInterceptor>().LifeStyle.PerWebRequest);
}
}
I now want to add the attribute to some arbitary page's code behind class and some arbitary virtual method, as in
[Log(Level.Info)]
protected string Login(string username, string password)
{
DoSomething();
}
This obviously doesn't work. Do I need to change the way I'm instantiating the page (its a page's code-behind class) to use a container? Or is it the way I'm registering the interceptors? I want to be able to use the interceptors on any class going forward and not have to tell the container about each and every class that I have in my application.
Short answer: it's not possible.
Long answer: due to the way ASP.NET Web Forms works, it doesn't let anyone interfere with the page instantiation. Some claim that using a custom PageHandlerFactory lets you do IoC, but this only lets you set properties after the page has been instantiated, which is simply not enough for proxying.
So runtime proxy libraries such as DynamicProxy or LinFu can't do anything about this. But you may be able to use compile-time aspect weavers, such as PostSharp.
Alternatively, make your code-behind as slim as possible, deferring actual logic to Windsor-managed components.