How to use an mvvmcross plugin such as the file plugin - plugins

I'm using mvvmcross version 6.4.1 to develop an app for IOS, Android, and WPF.
I've searched all over for my to use plugins. There seems to be no code examples. The documentation said to install the nuget in both my core and ui application projects. Which I did. Is there any special IOC registration/setup/or loading that needs to be done before I can use the plugin and how do I go about using the plugin? Do they get injected in the constructor or Do I have to manually pull them from the IOC container or new () them up.
I've installed nuget for the File plugin into my WPF UI and Core project. I added the IMvxFileStore to one of my core project's service constructor thinking it automagically gets added to the DI container, but it doesn't seem to get injected.
namespace My.Core.Project.Services
{
public class SomeService : ISomeService
{
private IMvxFileStore mvxFileStore;
public SomeService(IMvxFileStore mvxFileStore)
{
this.mvxFileStore = mvxFileStore;
}
public string SomeMethod(string somePath)
{
mvxFileStore.TryReadTextFile(somePath, out string content);
return content;
}
}
}
App.xaml.cs
using MvvmCross.Core;
using MvvmCross.Platforms.Wpf.Views;
...
public partial class App : MvxApplicatin
{
protected override void RegisterSetup()
{
this.RegisterSetupType<Setup<Core.App>>();
}
}
App.cs
using MvvmCross;
using MvvmCross.ViewModels;
using My.Core.Project.Services;
public class App: MvxApplication
{
public override void Initialize()
{
Mvx.IocProvider.RegisterType<ISomeService, SomeService>();
RegisterCustomAppStart<AppStart>();
}
}
AppStart.cs
using MvvmCross.Exceptions;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;
using My.Core.Project.ViewModels;
using System;
using System.Threading.Tasks;
....
public class AppStart : MvxAppStart
{
public AppStart(IMvxApplication application, IMvxNavigationService navigationService) : base(application, navigationService)
{}
public override Task NavigateToFirstViewModel(object hint = null)
{
try {
return NavigationService.Navigate<FirstPageViewModel>();
} catch {
throw e.MvxWrap("Some error message {0}", typeof(FirstPageViewModel).Name);
}
}
}
Setup.cs in WPF project
using MvvmCross;
using MvvmCross.Base;
using MvvmCross.Platforms.Wpf.Core;
using MvvmCross.Plugin.File;
using MvvmCross.Plugin.Json;
using MvvmCross.ViewModels;
using My.Wpf.Project.Services;
...
public class Setup<T> : MvxWpfSetup
{
public Setup() : base() {}
protected override IMvxApplication CreateApp()
{
return new Core.App();
}
protected override void InitializeFirstChange()
{
base.InitializeFirstChange();
Mvx.IocProvider.RegisterType<ISomeWpfSpecificService>(() => new SomeWpfSpecificService());
}
protected override void InitializeLastChange()
{
base.InitializeLastChange();
}
}
I'm expecting my service to load but instead, I get the error message
MvxIoCResolveException: Failed to resolve parameter for parameter mvxJsonConverter of type IMvxJsonConverter
NOTE: I get the same error message for both File and Json plugin, The plugin that gets listed first in the constructor gets the error message when the app trys to load.
Am I properly using or loading the plugin?
UPDATE: I manually registered the Plugins in the UI Setup.cs and it is working but I am not sure if this is the proper way to do it.
WPF UI project Setup.cs
using MvvmCross;
using MvvmCross.Base;
using MvvmCross.Platforms.Wpf.Core;
using MvvmCross.Plugin.File;
using MvvmCross.Plugin.Json;
using MvvmCross.ViewModels;
using My.Wpf.Project.Services;
...
public class Setup<T> : MvxWpfSetup
{
public Setup() : base() {}
protected override IMvxApplication CreateApp()
{
return new Core.App();
}
protected override void InitializeFirstChange()
{
base.InitializeFirstChange();
Mvx.IocProvider.RegisterType<ISomeWpfSpecificService>(() => new SomeWpfSpecificService());
Mvx.IoCProvider.RegisterType<IMvxFileStore, MvxFileStoreBase>();
Mvx.IoCProvider.RegisterType<IMvxJsonConverter, MvxJsonConverter>();
}
protected override void InitializeLastChange()
{
base.InitializeLastChange();
}
}

Yes you are using the plugin properly and I think that for now your solution to manually register your plugin is viable.
The root of the problem is located in the MvxSetup class. This class contains the method LoadPlugins which is responsible for loading the MvvmCross plugins which are referenced by your UI project. This is how LoadPlugins determines what plugins to load:
Get all assemblies that have been loaded into the execution context of the application domain.
Find types within these assemblies which contain the MvxPluginAttribute.
Now the problem occurs in step 1. In a .NET framework project, by default, your referenced assemblies won't be loaded into the execution context until you actually use them in your code. So if you don't use something from your MvvmCross.Plugin.File reference in your UI project it won't be loaded into your execution context and it won't be found in step 1 and thus it won't be registered by LoadPlugins. (good read: when does a .NET assembly Dependency get loaded)
One way I have tested this is by doing this:
protected override void InitializeFirstChance()
{
// Because a type of the MvvmCross.Plugin.File.Platforms.Wpf reference is
// used here the assembly will now get loaded in the execution context
var throwaway = typeof(Plugin);
base.InitializeFirstChance();
}
With the above code you don't have to manually register the Plugin.
There has been a pull request to fix this in the MvvmCross framework but this has been reverted later since it caused problems on other platforms.
In other platforms the plugin assemblies will get loaded into the execution context without any tricks so I would say updating the MvvmCross documentation stating you have to register your plugin manually for WPF would be useful for other developers in the future.

Related

EF Core Migrations in Azure Function startup

According to https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection the service provider should not be used until AFTER the startup has completed running. Indeed, if I try to get a registered service it will fail.
Example:
[assembly: FunctionsStartup(typeof(Startup))]
namespace Fx {
public sealed class Startup : FunctionsStartup {
public override void Configure(IFunctionsHostBuilder builder) {
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddEnvironmentVariables();
var configuration = configurationBuilder.Build();
builder.Services.AddInfrastructure(configuration);
builder.Services.AddApplication();
var serviceProvider = builder.Services.BuildServiceProvider();
DependencyInjection.AddDatabase(serviceProvider).GetAwaiter().GetResult();
}
}
}
public static class DependencyInjection {
public static async Task AddDatabase(IServiceProvider services) {
using var scope = services.CreateScope();
var serviceProvider = scope.ServiceProvider;
var context = serviceProvider.GetRequiredService<ApplicationDbContext>();
//Error generated here
if (context.Database.IsSqlServer()) {
await context.Database.MigrateAsync();
}
await ApplicationDbContextSeed.SeedSamplePersonnelDataAsync(context);
}
public static IServiceCollection AddInfrastructure(
this IServiceCollection services,
IConfiguration configuration) {
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
return services;
}
}
This produces the following error
Microsoft.EntityFrameworkCore: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.
Is there a good option for migrating and seeding during startup?
The easiest way I found to run code after startup was by registering a custom IWebJobsStartup by using the WebJobsStartupAttribute (the FunctionsStartupAttribute actually also inherits from this attribute). In the WebJobsStartup class you'll need to register your extension using the AddExtension where you are able to use dependency injection and seed your database. My code:
[assembly: WebJobsStartup(typeof(DbInitializationService), "DbSeeder")]
namespace Our.Database.Seeder
{
public class DbInitializationService : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.AddExtension<DbSeedConfigProvider>();
}
}
[Extension("DbSeed")]
internal class DbSeedConfigProvider : IExtensionConfigProvider
{
private readonly IServiceScopeFactory _scopeFactory;
public DbSeedConfigProvider(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public void Initialize(ExtensionConfigContext context)
{
using var scope = _scopeFactory.CreateScope();
var dbContext = scope.ServiceProvider.GetService<YourDbContext>();
dbContext.Database.EnsureCreated();
// Further DB seeding, etc.
}
}
}
According to your code, I assume that you're building something aligned to the CleanArchitecture Repository on Github. https://github.com/jasontaylordev/CleanArchitecture
The main difference between this repo and your apporach, is that you're obviously not using ASP.NET, which is not a problem at all, but requires a little bit more configuration work.
The article already mentioned (https://markheath.net/post/ef-core-di-azure-functions) refers another blogpost (https://dev.to/azure/using-entity-framework-with-azure-functions-50aa), which briefly explains that EntityFramework Migrations are not capable of auto-discovering your migrations in an Azure Function. Therefore, you need to implement an instance of IDesignTimeDbContextFactory. I also stumbled upon it in the microsoft docs:
https://learn.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli#from-a-design-time-factory
You could for example place it inside your Infrastructure\Persistence\Configurations folder. (Once again, I'm only assuming that you're following the CleanArchitecture repo structure)
DI in AZURE Functions
Caveats
A series of registration steps run before and after the runtime processes the startup class. Therefore, keep in mind the following items:
The startup class is meant for only setup and registration. Avoid using services registered at startup during the startup process. For instance, don't try to log a message in a logger that is being registered during startup. This point of the registration process is too early for your services to be available for use. After the Configure method is run, the Functions runtime continues to register additional dependencies, which can affect how your services operate.
The dependency injection container only holds explicitly registered types. The only services available as injectable types are what are setup in the Configure method. As a result, Functions-specific types like BindingContext and ExecutionContext aren't available during setup or as injectable types

Shiny.SpeechRecognition (Plugin) integration with MvvmCross and Xamarin.Forms

I have implemented the Shiny Speech Recognition plugin in a MvvmCross Xamarin.Forms solution.
=> https://github.com/shinyorg/shiny
Nuget => https://www.nuget.org/packages/Shiny.SpeechRecognition/1.0.0.357-beta
I'm trying to use the ISpeechRecognition in the MvxViewModel constructor as it work on the Prism Sample on GitHub.
=> https://github.com/shinyorg/shinysamples/tree/master/Samples/Speech
I have implemented The integration plugin for MvvmCross.
=> https://www.nuget.org/packages/Shiny.Integrations.MvvmCross/1.0.0.375-beta
Now I want to know how I can use the Speech Recognition service.
Can I have something like:
public YourViewsModel(IMvxNavigationService navigationService, ISpeechRecognizer speechRecognizer)
{
_navigationService = navigationService;
_speechRecognizer = speechRecognizer;
}
Or maybe dependency injection with the register dependence like:
Mvx.IoCProvider.LazyConstructAndRegisterSingleton<ISpeechRecognizer, SpeechRecognizerImpl>();
What I want to know, if someone have already Shiny plugin Xamarin.Forms is how used the service ?
I have create a startup Shiny class.
public class InitializeShiny : ShinyStartup
{
public override void ConfigureServices(IServiceCollection services)
{
services.UseSpeechRecognition(); // implement Speech Recognition service.
}
}
Then initialize a top level custom Application definition.
In the OnCreate() method:
// Initialisation Shiny Plugin.
AndroidShinyHost.Init(Application, new InitializeShiny(), services =>
{
services.UseSpeechRecognition();
});
So now in our ViewModel we can inherit from ShinyMvxViewModel to start the ViewModel.
public class YourViewModel : ShinyMvxViewModel {}
All is working perfectly. The application is launched without problem.
This doesn't work:
public YourViewsModel(IMvxNavigationService navigationService, ISpeechRecognizer speechRecognizer)
I can create the constructor because it doesn't recognize ISpeechRecognizer.
Only
public YourViewsModel(IMvxNavigationService navigationService)
is working.
This doesn't work too:
private ISpeechRecognizer _speechRecognizer;
_speechRecognizer = Mvx.IoCProvider.Resolve<ISpeechRecognizer>();
The error is it doesn't have implemention for ISpeechRecognizer. I don't know.
I have initialise the lazy register in the override method.
protected override void InitializePlatformServices()
Mvx.IoCProvider.LazyConstructAndRegisterSingleton<ISpeechRecognizer, SpeechRecognizerImpl>();
Thank for the help.
Zebiphire

Recommended way to register custom serializer with StateManager

In the pre-GA version of Service Fabric I was registering a custom serializer like this:
protected override IReliableStateManager CreateReliableStateManager()
{
IReliableStateManager result = new ReliableStateManager(
new ReliableStateManagerConfiguration(
onInitializeStateSerializersEvent: InitializeStateSerializers));
return result;
}
private Task InitializeStateSerializers()
{
StateManager.TryAddStateSerializer(new KFOBinarySerializer());
return Task.FromResult(false);
}
However, the CreateReliableStateManager method was removed in the GA version. I've struggled to get something working in its place. Currently I'm calling
StateManager.TryAddStateSerializer(new KFOBinarySerializer());
from within the service's RunAsync method, which appears to work fine.
What is the recommended way to register a custom serializer?
TryAddStateSerializer is deprecated. Anyone know if this is because custom serialization support will go away or if it will simply be supported through some other mechanism?
You can create the state manager in the StatefulService's constructor (full example here):
class MyService : StatefulService
{
public MyService(StatefulServiceContext serviceContext)
: base(serviceContext, CreateReliableStateManager()) { }
private static IReliableStateManager CreateReliableStateManager() { ... }
}
Regarding the deprecated API, Microsoft says it's safe to use, but it will change in the future.

PostSharp aspect not working when implement into assembly and calling that method

I have two three projects
1. framework
2. Repository
3. MVC Project
In framework project i implemented aspect
namespace FrameworkHelper.TestAspect
{
[Serializable]
[MulticastAttributeUsage(MulticastTargets.Method)]
public class CacheAspect : OnMethodBoundaryAspect
{
// This field will be set by CompileTimeInitialize and serialized at build time,
// then deserialized at runtime.
public string methodName;
// Method executed at build time.
public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
{
this.methodName = method.DeclaringType.FullName + "." + method.Name;
}
// This method is executed before the execution of target methods of this aspect.
public override void OnEntry(MethodExecutionArgs args)
{
}
// This method is executed upon successful completion of target methods of this aspect.
public override void OnSuccess(MethodExecutionArgs args)
{
}
}
}
And aspect implemented into repository project
[TestAspect]
public List<string> TestMethod()
{
}
, When we calling method TestMethod() from MVC project , aspect not working , what's wrong on this code.
Its working fine when we use with one assembly.
I created new project for test the scanario and uploading project into github you can go through and can test home index controller.
https://github.com/virenderkverma/PostSharp-Examples
Project - PostSharpMultipleAssemblyCall
You can test this project
Sorry Daniel, Its working fine with latest version of postsharp , right now using
Postsharp of 3.1.50.9
Before i used 3.1.48 , May be that's problem with old version.
Thanks

This 404 seems unavoidable - what am I doing wrong? [Ninject 2.0 with ASP.NET MVC 2 on .NET 4]

I downloaded the fairly new Ninject 2.0 and Ninject.Web.Mvc (targeting mvc2) sources today, and successfully built them against .NET 4 (release configuration). When trying to run an application using Ninject 2.0, i keep getting 404 errors and I can't figure out why.
This is my global.asax.cs (slightly shortified, for brevity):
using ...
using Ninject;
using Ninject.Web.Mvc;
using Ninject.Modules;
namespace Booking.Web
{
public class MvcApplication : NinjectHttpApplication
{
protected override void OnApplicationStarted()
{
Booking.Models.AutoMapperBootstrapper.Initialize();
RegisterAllControllersIn(Assembly.GetExecutingAssembly());
base.OnApplicationStarted();
}
protected void RegisterRoutes(RouteCollection routes)
{
...
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Entry", action = "Index", id = "" }
);
}
protected override IKernel CreateKernel()
{
INinjectModule[] mods = new INinjectModule[] {...};
return new StandardKernel(mods);
}
}
}
The EntryController exists, and has an Index method that simply does a return View(). I have debugged, and verified that the call to RegisterAllControllersIn() is executed. I have also tried to use Phil Haacks Routing debugger but I still get a 404.
What do I do to find the cause of this?
Are your routes being registered? Is that being called from the base class?
Also make sure that you are registering your controllers properly. I'm not sure how Ninject's controller factory expects it, but it might require a specific name or something.