Skip to content
This repository has been archived by the owner on Dec 18, 2017. It is now read-only.

You must call EnsureLoaded on the File plugin before using the DownloadCache #119

Closed
tbalcom opened this issue Jul 6, 2016 · 28 comments
Closed

Comments

@tbalcom
Copy link

tbalcom commented Jul 6, 2016

The update from 4.2.0 to 4.2.1 seems to sometimes cause issues if the project uses the DownloadCache and File plugins. When this happens an exception is thrown during setup

You must call EnsureLoaded on the File plugin before using the DownloadCache

During the update NuGet shows an error claiming the various Bootstrap files can not be found. The files are present and have not been modified from the originals. It's not clear to me why NuGet can't see these files.

I've asked about this on Stackoverflow.

It seems like the DownloadCache plugin has a dependency on the File plugin but the DownloadCache is loaded before the File plugin has been loaded.

One workaround is to manually remove the DownloadCache and File bootstrap loaders, then manually call EnsureLoaded on the plugins in the correct order:

    protected override void InitializeLastChance()
    {
        base.InitializeLastChance();
        MvvmCross.Plugins.File.PluginLoader.Instance.EnsureLoaded();
        MvvmCross.Plugins.DownloadCache.PluginLoader.Instance.EnsureLoaded();
    }

Steps to reproduce

  1. Update from 4.2.0 to 4.2.1 via NuGet.
  2. Run the project.
  3. An exception is thrown during setup.

Expected behavior

The plugins should load automatically in the correct order.

Actual behavior

An exception is thrown during setup.

Configuration

Version: 4.2.1

Platform: Android

@Cheesebaron
Copy link
Member

Have you tried deleting the bootstrap files and adding them again?

@tbalcom
Copy link
Author

tbalcom commented Jul 7, 2016

Deleting the bootstrap files and adding the packages back via NuGet didn't change anything. The Droid project has the following plugins:

  1. Color
  2. Json
  3. Location
  4. Messenger
  5. ResourceLoader
  6. Sqlite
  7. WebBrowser

How does MvvmCross ensure the plugins are loaded in the correct order when one plugin depends on another plugin?

@Zolee007
Copy link

I have run into the same problem, however I didn't delete the Bootstrap files, I have overriden the DownloadCachePluginBootstrap's Loadmethod to ensure the File plugin gets loaded before the DownloadCache plugin.

public class DownloadCachePluginBootstrap
        : MvxPluginBootstrapAction<MvvmCross.Plugins.DownloadCache.PluginLoader>
    {
        protected override void Load(IMvxPluginManager manager)
        {
            MvvmCross.Plugins.File.PluginLoader.Instance.EnsureLoaded();
            base.Load(manager);
        }
    }

@IlSocio
Copy link
Contributor

IlSocio commented Jul 15, 2016

I faced the same issue too, thanks for the fix Zolee

@alfredp77
Copy link

alfredp77 commented Jul 16, 2016

I'm on iOS, and MvvmCross 4.2.2. Putting in just Zolee's workaround doesn't work, I got the following exception:

MvvmCross.Platform.Exceptions.MvxException: Could not find plugin loader for type MvvmCross.Plugins.File.PluginLoader

In addition to Zolee's workaround, I need to do the following:

  • Force the File.PluginLoader to be registered by overriding AddPluginsLoaders in Setup:
protected override void AddPluginsLoaders(MvxLoaderPluginRegistry registry)
{
    registry.Register<MvvmCross.Plugins.File.PluginLoader, MvvmCross.Plugins.File.iOS.Plugin>();
    base.AddPluginsLoaders(registry);
}
  • Subclass MvxLoaderPluginBootstrapAction and override PreLoad method to prevent the File.PluginLoader to be re-registered the second time:
    public class NoPreLoadPluginBootstrapAction<TPlugin, TPlatformPlugin> : MvxLoaderPluginBootstrapAction<TPlugin, TPlatformPlugin> where TPlugin : IMvxPluginLoader where TPlatformPlugin : IMvxPlugin
    {
        protected override void PreLoad(IMvxPluginManager manager)
        {
           // do nothing;
        }
    }
  • Use that NoPreLoadPluginBootstrapAction instead of the MvxLoaderPluginBootstrapAction for FilePluginBootstrap:
    public class FilePluginBootstrap
        : NoPreLoadPluginBootstrapAction<MvvmCross.Plugins.File.PluginLoader, MvvmCross.Plugins.File.iOS.Plugin>
    {
    }

@alexsorokoletov
Copy link

Just ran into the same issue, @Zolee007 's fix helped.

@Zolee007
Copy link

Zolee007 commented Jul 28, 2016

Now that I also started to develop the iOS side of my application, I also run into the problem as @alfredp77 . I looked into the source code a bit to try to figure out the difference between the Android and iOS plugin loading.

I found that MvxIosSetup's CreatePluginManager method creates a simple MvxPluginManager, while MvxAndroidSetup's CreatePluginManager method creates an MvxFilePluginManager instance. Based on this fact I overridden the CreatePluginManager method of the MvxIosSetup class (in the actual Setup class) to use an MvxFilePluginManager instance as well:

protected override IMvxPluginManager CreatePluginManager()
{
    return new MvxFilePluginManager(".iOS", ".dll");
}

By doing this in addition of my previously posted workaround makes iOS working as well.

@flaviusdemian
Copy link

@alfredp77 @Zolee007 I get this on iOS now... On a project it worked, on the current one I get this:

System.ArgumentException: Cannot create an instance of UpWorky.Mobile.iOS.Employee.NoPreLoadPluginBootstrapAction`2[TPlugin,TPlatformPlugin] because Type.ContainsGenericParameters is true.

Do you have some suggestions?

Thanks

@trevorchunestudy
Copy link

I tried @Zolee007 's solution and had same problem that @alfredp77 had. As opposed to the latter workaround, I just went back and used the old manual registration in Setup.cs which has changed slightly from previous versions. I also removed the Bootstrap files for now until this get's sorted out.

This works fine for 4.2.2.

        protected override void AddPluginsLoaders(MvxLoaderPluginRegistry registry)
        {
            registry.Register<MvvmCross.Plugins.DownloadCache.PluginLoader, MvvmCross.Plugins.DownloadCache.iOS.Plugin>();
            registry.Register<MvvmCross.Plugins.File.PluginLoader, MvvmCross.Plugins.File.iOS.Plugin>();
            base.AddPluginsLoaders(registry);
        }

        protected override void InitializeLastChance()
        {

            base.InitializeLastChance();
            MvvmCross.Plugins.File.PluginLoader.Instance.EnsureLoaded();
            MvvmCross.Plugins.Json.PluginLoader.Instance.EnsureLoaded();
            PluginLoader.Instance.EnsureLoaded();
        }

@flaviusdemian
Copy link

I will check in a few hours. Thanks

@lisannefoget
Copy link

I ran into the same issue as @alfredp77, and @Zolee007's first workaround fixed it for me on Android. Once I started on iOS, @Zolee007 's workaround to add this into the Setup.cs worked great!

protected override IMvxPluginManager CreatePluginManager()
{
return new MvxFilePluginManager(".iOS", ".dll");
}

I didn't have to remove any of the original Bootstrap files or manually register the plugin loaders.

@Cheesebaron
Copy link
Member

@SeeD-Seifer any idea what is going on? I haven't debugged this yet, but seems like the MvxPluginManager does not pick up some plugins.

@Cheesebaron
Copy link
Member

OK. So I've been debugging a bit. The problem seems to be that the Load method is called in the order the bootstrap files are discovered?

So if I remove the boostrap files for File and DownloadCache and make a new where both are defined in this order:

public class FilePluginBootstrap
    : MvxPluginBootstrapAction<MvvmCross.Plugins.File.PluginLoader>
{
}

public class DownloadCachePluginBootstrap
    : MvxPluginBootstrapAction<MvvmCross.Plugins.DownloadCache.PluginLoader>
{
}

It seems to work fine.

If I swap the order in the file to:

public class DownloadCachePluginBootstrap
    : MvxPluginBootstrapAction<MvvmCross.Plugins.DownloadCache.PluginLoader>
{
}

public class FilePluginBootstrap
    : MvxPluginBootstrapAction<MvvmCross.Plugins.File.PluginLoader>
{
}

💥 everything blows up...

How this worked before I have no idea, maybe by coincidence? I've reviewed the changes @SeeD-Seifer made to the plugins registry in MvvmCross/MvvmCross@ffd2b33 and I don't see how it would change anything.

Now! On the other side this 4e8ea9f has added more stuff to the plugin loader, which matches the crash I get

@alexshikov
Copy link

@Cheesebaron I'll review the thread and the code later today and let you know when find the reason of the issue. Highly suspect this is somehow related to my changes.

@Cheesebaron
Copy link
Member

@SeeD-Seifer I am not sure it is actually your changes that did it. However, rather @promontis' changes which explicitly ask for the File plugin.

I think we need to do something about discovering dependencies in Plugins and fix load order.

@alexshikov
Copy link

@Cheesebaron you was right, @promontis commit broke the plugin loading.

Apart from direct fix I have noticed few things:

  1. DownloadCache plugin depends on File plugin implementation, but there is no NuGet dependency. I understand why it's exist in current way, but wouldn't it be easier to make DownloadCache dependent on File? I hardly believe there is anybody using custom implementation of IMvxStore instead of File plugin.
  2. Bootstrap files actually loaded in random order. There is a project in MvvmCross-Samples repo WorkingWithCollection that uses both plugins but loads them in correct order so the issue did not reproduced there. Does it make sense to force bootstrap loader to sort found types by name and then load them? I'm not sure how often it is important, but it's annoying when there is a case with random behaviour...

@promontis
Copy link
Contributor

Ah yes, the commit I did eager loads the File plugin, which it shouldn't do. The fix from @SeeD-Seifer fixes that, awesome!

I agree that the dependencies between plugins, the loading of them, and the order in which they load should be fixed. Already bumped my head several times because of it. I did talk to @martijn00 at the last mvvmcross meetup we had about this... maybe he can jump into this conversation? I thought the solution he proposed was to actually remove the bootstrap loaders and initialize the plugins yourself in the correct order.

@martijn00
Copy link
Contributor

@promontis Yes, to manually register the plugins could be a temporary solution. I would like to look into how we can make a proper fix in combination with moving the plugins to Net Standard (PCL) based setup. @Cheesebaron already looked into this a bit in #63 (comment)

@alexshikov
Copy link

To ensure plugins loaded in the same order MvxBootstrapRunner should be updated.
This is the method from MvxBootstrapRunner.cs:

        public virtual void Run(Assembly assembly)
        {
// types order is not determined.
            var types = assembly.CreatableTypes()
                                .Inherits<IMvxBootstrapAction>();

            foreach (var type in types)
            {
                this.Run(type);
            }
        }

Simple solution is to always sort types by Name or FullName.

@Cheesebaron
Copy link
Member

That would solve it? F comes after D?

@promontis
Copy link
Contributor

Haha... that's what I first did as well :) sorting the plugins by name. Now I just manually register the plugins, so that I minimize the performance hit of using reflection.

@alexshikov
Copy link

@Cheesebaron when I launched MvvmCross-Samples/WorkingWithCollection project which uses File and DownloadCache plugins I was surprised, it didn't crash with MvvmCross 4.2.3 NuGet packages. It appeared, for some reason MvxBootstrapRunner loaded File plugin first, just because assembly.CreatableTypes() returned this type first.

Hacking MvxBootstrapRunner to sort types lead to stable reproduce of the issue. Can't explain why.

@flaviusdemian
Copy link

flaviusdemian commented Sep 24, 2016

Hy Guys, this is a bit confusing...can somebody put a gist with the whole correct steps? I am using the Support JASlider also. Maybe this adds some problems as well.

`
Window = new UIWindow(UIScreen.MainScreen.Bounds);

            var setup = new Setup(this, Window);
            setup.Initialize();

            var startup = Mvx.Resolve<IMvxAppStart>();
            startup.Start();

            Window.MakeKeyAndVisible();`

In the Setup class I have

protected override IMvxIosViewPresenter CreatePresenter() { return new MvxSidePanelsPresenter((MvxApplicationDelegate)ApplicationDelegate, Window); }

Should the download cache plugin bootStrapper look like this?

public class DownloadCachePluginBootstrap : MvxLoaderPluginBootstrapAction<MvvmCross.Plugins.DownloadCache.PluginLoader, MvvmCross.Plugins.DownloadCache.iOS.Plugin> { }

I have upgraded the Collections sample to 4.2.3 and it works but in our project it does not work... This is very strange.

@Cheesebaron
Copy link
Member

This is fixed in 4.3.0

@flaviusdemian
Copy link

Thank you very much.

@luigisaggese
Copy link

luigisaggese commented Apr 26, 2017

@Cheesebaron I have same issue on 4.4.0 (In release mode -> Linking All assemblies)
This workaround still work to solve (inside Setup.cs)

protected override IMvxPluginManager CreatePluginManager()
{
	return new MvxFilePluginManager(".iOS", ".dll");
}

@Cheesebaron
Copy link
Member

So MvxFilePluginManager gets linked away?

This issue is about order of plugins being loaded. Not linking issues.

@luigisaggese
Copy link

Yes MvxFilePluginManager is linked away and with this method overriding i have solved it

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests