Using NuGet for Application Plug-Ins
Comicster now uses NuGet to manage its plug-ins and skins (which I collectively call "extensions"). Using NuGet in your own application is easy! Let's go through the core parts:
First you'll need a reference to NuGet.Core. You can install this using NuGet itself, but be aware that as of this writing (version 1.4) the assembly is only for the full .NET Framework "extended profile" - Client Profile applications won't build if you use NuGet.Core. I'm currently using a custom build of the assembly until version 1.5 is released.
Once you've referenced NuGet.Core, you'll need a repository from which to install packages. There are a couple of different kinds, but the easiest way to get started is to point to a local folder on your machine. Either way, use the PackageRepositoryFactory
to spin one up:
var repository = PackageRepositoryFactory.Default.CreateRepository(@"C:\Packages);
Next you'll need a package manager with which to install, update and remove packages. You'll need to tell it where to get them from (the repository) and where to put them (the destination folder):
var manager = new PackageManager(repository, @"D:\Extensions");
In Comicster, I use "My Documents\Comicster\Extensions" for the destination, calculated like this:
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Comicster", "Extensions")
Now that you have a package manager, you can fetch a list of packages to show your user. Comicster has an ExtensionsViewModel
that does this. Here's its "Refresh" method:
IEnumerable<IActivity> Refresh()
{
PageMessage = "Retrieving extensions...";
var fetcher = new PackageFetcher(_packageManager, _pageSize, _offset, SelectedFilter.Key);
yield return fetcher;
_packages.Clear();
_packages.AddRange(fetcher.Packages
.Select(p => new ExtensionViewModel(this, MessengerInstance, p, _packageManager)));
// the total count of packages, not the same as _packages.Count!
_packageCount = fetcher.PackageCount;
}
A couple of things to look at here.
First, note that my Refresh method is declared to return an IEnumerable<IActivity>
. Because the act of fetching packages should be asynchronous (important if you're going off to a web feed), it's using the coroutine support in MadProps.MvvmLight. I've made a PackageFetcher
activity to do the heavy lifting. I won't post the entire code here, but the crux of it looks something like this:
_packageManager.SourceRepository.GetPackages()
.Where(p => p.Tags.Contains(_filter))
.OrderBy(p => p.Id)
.Skip(_offset)
.Take(_pageSize)
.ToList();
Phew! The GetPackages()
method returns an IQueryable<IPackage>
, which means we can perform LINQ operations on it from the client and only the overall query will be sent to the "server" (in the case of using web feeds). You can see that I'm paging my results here using Skip()
and Take()
.
In Comicster I wrap packages up in an ExtensionViewModel
instance which has commands for installing, updating and removing packages. Installing looks like this:
void Install()
{
try
{
_packageManager.InstallPackage(_package, false);
_host.NotifyPackages();
}
catch (Exception ex)
{
MessengerInstance.Send(new GalaSoft.MvvmLight.Messaging.DialogMessage(ex.Message, null));
}
}
So the first thing I do there is attempt to install the package, passing false
for the "ignoreDependencies" command because I want them if they're needed.
I then notify the host ViewModel (the ExtensionsViewModel
) that a package has been installed, so that it can update the view for all the other extensions, in case a dependency has also been installed.
Lastly, in the event of an exception I simply show a message to the user (using MVVM Light's messaging system).
Updating and removing packages work exactly the same way. IPackageManager
gives you UpdatePackage()
and UninstallPackage()
methods that work predictably.
The last thing to talk about in this post is the package repository we used right at the top. Comicster, of course, doesn't use a local folder for its package source - it uses a web feed. Well, actually it uses both. If you want plug-in developers to be able to test their packages before uploading them, you need to give them an easy way to do that.
To use two different repositories simultaneously you can use the AggregateRepository
class. Here's what Comicster does:
var repository = new AggregateRepository(
new [] {
PackageRepositoryFactory.Default.CreateRepository("http://extend.comicster.net/nuget"),
PackageRepositoryFactory.Default.CreateRepository(packagesFolder)
});
You can see I'm referencing a web feed (at the Comicster Extensions site) as well as my local folder, but I'm simply using the PackageRepositoryFactory
to create the repositories. That's pretty easy!
To learn about hosting your own NuGet feed, I recomment a read through of this post from Phil Haack. His NuGet.Server package makes it a snap!
In a future post I'll write about integrating the packages you've downloaded into your app using MEF. Stay tuned!
Trackbacks
- Comment utiliser NuGet pour gérer des extensions dans ses propres applications | Around computing | http://muibiencarlota.wordpress.com/2012/02/24/comment-utiliser-nuget-pour-gerer-des-extensions-dans-ses-propres-applications/
- Dew Drop – June 19, 2011 | Alvin Ashcraft's Morning Dew | http://www.alvinashcraft.com/2011/06/19/dew-drop-june-19-2011/
- Extend NuGet Command Line | WP7 Developers Links | http://wpdevelopers.ginktage.com/2011/07/15/extend-nuget-command-line/
- Extend NuGet Command Line « Mas-Tool's Favorites | http://mas-tool.com/?p=1936
- rimonabantexcellence site title | http://www.rimonabantexcellence.com/t.php?ahr0cdovl21hdhroyw1pbhrvbi5vcmcvbnvnzxqtzm9ylxbsdwctaw5z
- Using NuGet for Application Plug-Ins « Mas-Tool's Favorites | http://mas-tool.com/?p=2752