CHAPTER 10

image

NuGet as a Protocol

So far, you’ve seen how to consume and create NuGet packages and how to host your own feed containing packages. Using NuGet, we’ve managed to set up a means of continuous package delivery and extended the NuGet command line and Package Manager Console.

The concepts and techniques demonstrated in the previous chapters focused on using NuGet as a means of managing dependencies when developing software using Visual Studio. We’ve looked at NuGet as a package manager. How about we change our perspective and look at NuGet as a protocol for distributing packages?

You’ve already seen that complete deployments can be shipped as NuGet packages and that complete software installations can be distributed through NuGet (see Chapter 7). Why not leverage these techniques ourselves? All NuGet components have been built around one central assembly, NuGet.Core, which provides the base functionalities used in NuGet: working with feeds (or repositories) and installing and uninstalling packages based on such feeds.

This chapter will demonstrate how you can use NuGet.Core in your applications. If you are working with any form of plug-ins that users can install into your application or want to work with a self-updating executable (much like nuget.exe update -self), the techniques described in this chapter will be of use to you. In this chapter, we’ll create a simple ASP.NET Model-View-Controller (MVC) application that supports loading plug-ins from external assemblies, and we’ll distribute these plug-ins through NuGet.

Understanding Protocols

Let’s have a look at Wikipedia’s definition for a protocol:

A communications protocol is a system of digital message formats and rules for exchanging those messages in or between computing systems and in telecommunications. A protocol may have a formal description.

Protocols may include signaling, authentication, and error detection and correction capabilities.

Wikipedia, http://en.wikipedia.org/wiki/Communications_protocol

If we break down the definition for a protocol, there are two important concepts to note: exchanging messages and formal description. When you think of NuGet, it supports exactly that: NuGet provides a means of exchanging messages through the use of a feed containing packages. These feeds conform to the OData feed format, a formal description of what a feed should look like. Packages are also created according to a format description: the Open Packaging Conventions are used to create a compressed archive containing multiple files and the relations between them. The NuGet package manifest adds a layer of information to these packages, containing data such as package authors, versioning information, and tags.

That leaves one conclusion to make: NuGet is a protocol—not a low-level protocol like TCP, but a high-level protocol for exchanging packages between a server and a client. If you take into account that packages can be pushed onto a feed, that also means an exchange between client and server is possible. Whereas TCP packages transport only a few bytes, NuGet packages can potentially transport gigabytes of data. NuGet provides a protocol for distributing those packages through a feed.

To make this explanation more clear, let’s look at an example. Paint.NET, a well-known image editor written in .NET and available freely at www.getpaint.net, has plug-ins. If you want to create an image effect filter that performs specific actions on the input image, you can do so. It’s a pity, though, that community-contributed plug-ins are available only on a bulletin board, from which you have to manually download the plug-in and copy it into Paint.NET’s installation directory.

Of course, this could be done by using a web site where a zip file containing a plug-in is available for download. Using some C# or VB.NET code, it would be easy to download these files and copy them to a specific folder. But why go through all the hassle of supporting proxies and authentication and creating conventions on how a plug-in should be compressed?

It would make sense to use NuGet as a protocol in this situation. If the members of the team behind Paint.NET chose NuGet as a protocol, they could provide a user interface enabling users to pick the plug-ins to install from a feed (a NuGet feed, but that fact would be invisible to the user). Using NuGet under the cover, Paint.NET would be able to download the plug-in packages and copy the contents to the Paint.NET plug-ins folder. Authors of plug-ins could publish their plug-ins to the same NuGet feed to share their work with the world. All the infrastructure to establish this scenario is available in NuGet.Core!

Similarly, in the next section, we’ll create an ASP.NET MVC application supporting plug-ins and distribute these plug-ins through a NuGet feed.

Creating a Pluggable Application by Using NuGet

This chapter will make use of a sample application called SearchPortal, for which you can find the source code in the Source Code/Download area of the Apress web site (www.apress.com). SearchPortal is a web application written in ASP.NET MVC, which can be installed in any company and serves as a portal to one or more search engines. As Figure 10-1 demonstrates, anyone navigating to the SearchPortal home page can enter a string to search for, select a search engine, and perform a web search based on that information.

9781430260011_Fig10-01.jpg

Figure 10-1.SearchPortal home page, allowing a user to specify a search string and select a search engine that should be used to perform the search

The list of search engines that can be used is specific to the IT department installing SearchPortal on its servers. Some companies may choose to allow searches using only Bing, while others may allow Bing, Google, and maybe even their own internal search engine. Figure 10-2 shows you a search for Apress Pro NuGet after selecting Bing from the list of search engines.

9781430260011_Fig10-02.jpg

Figure 10-2.Using Bing to search for Apress Pro NuGet

An administrator can log in to the administration area of the application (conveniently located at /administration). As Figure 10-3 shows, the administration area features a list of available search engines that can be installed or uninstalled. Using this page, the administrator picks the search engines to be shown in the drop-down list on the SearchPortal home page.

9781430260011_Fig10-03.jpg

Figure 10-3.The SearchPortal administration area

The Plugins list shown in the administrative area is based on a NuGet feed located at www.myget.org/F/pronuget, the same feed that we’ve used as a sample in previous chapters. All plug-ins for SearchPortal are, in fact, NuGet packages containing a search engine that can be plugged into SearchPortal. This enables any user of SearchPortal to create a search engine plug-in that can be installed in any SearchPortal deployment by administrators of SearchPortal. The list of packages on the MyGet feed can be seen in Figure 10-4.

9781430260011_Fig10-04.jpg

Figure 10-4.The pronuget feed hosted on MyGet and listing three SearchPortal search engine plug-ins

Defining Architecture and Technologies

Before we dive into how SearchPortal is developed, it is important to identify a suitable architecture and pick suitable technologies to support that architecture. SearchPortal uses two concepts that require a well-thought-out architecture:

  • SearchPortal has to be extensible. The application architecture should support loading plug-ins from assemblies that were not originally shipped with the SearchPortal installation files. Extensions should be discovered and loaded by the application and integrated at runtime.
  • Plug-ins should be easily discoverable. Distributing plug-ins to SearchPortal administrators should be simple and easy to do.

To build SearchPortal, we will make use of the following technologies:

  • ASP.NET MVC will be used to develop the web interface for SearchPortal.
  • Microsoft’s Managed Extensibility Framework (MEF) will be used to make the application extensible. Living in the System.ComponentModel.Composition namespace, MEF is a framework that enables your application to load external assemblies at runtime and integrate them into your application.
  • NuGet will be used for plug-in distribution. Plug-ins containing additional search engines will be compiled into an assembly and packaged into a NuGet package.

image Tip  If you are not familiar with the Managed Extensibility Framework (MEF), refer to Maarten’s MEF Is Cool as ICE blog post at http://blog.maartenballiauw.be/post/2010/03/04/MEF-will-not-get-easier-its-cool-as-ICE.aspx. Using MEF, classes can import dependencies by adding the [Import] attribute to a property. These imports are satisfied by composing the application and looking for matching exports, which have an [Export] attribute. Have a look at this blog post to quickly grasp the ideas and principles behind MEF.

Figure 10-5 shows a schematic view of the SearchPortal architecture. The SearchPortal.Web application will be created as an ASP.NET MVC application using Visual Studio. It will make use of MEF to load search engine plug-ins such as Bing or Google. These plug-ins will be distributed using NuGet and can be installed through an administration area, which makes use of the NuGet.Core assembly.

9781430260011_Fig10-05.jpg

Figure 10-5.Schematic view of the SearchPortal architecture

Defining the ISearchEngine Interface

As discussed in the previous section of this chapter, SearchPortal will make use of MEF to load and integrate plug-ins at runtime. Since MEF is part of the .NET Framework, it is easy to make an application support a form of plug-ins:

  • Add an assembly reference to System.ComponentModel.Composition.
  • Create an interface for the search engines plug-ins.

The first part, adding a reference to a .NET Framework assembly to a Visual Studio project, should be easy. Let’s do something more interesting; let’s define an interface for search engine plug-ins.

The ISearchEngine interface in Listing 10-1 shows you an interface that exposes a property and a method. This interface has to be implemented by the plug-ins to be discoverable by the SearchPortal application. The Name property should return the name of the search engine plug-in—for example, Bing. The GenerateSearchUri method should return a Uri object based on a given searchTerm. For example, when searching for Pro NuGet, the GenerateSearchUri should return an URL in the form of www.bing.com/search?q=Pro%20Nuget. This URI can then be used by SearchPortal to initiate the actual search on the selected search engine.

Listing 10-1.  The ISearchEngine Interface

[InheritedExport]
public interface ISearchEngine
{
    string Name { get; }

    Uri GenerateSearchUri(string searchTerm);
}
 

Note that the ISearchEngine interface has the [InheritedExport] attribute, which will tell MEF that any class implementing the ISearchEngine interface will be exposed as a plug-in and can be loaded at runtime. This is the basis for the plug-in architecture in SearchPortal.

An example ISearchEngine implementation is shown in Listing 10-2. The Bing class implements the ISearchEngine interface, and because that interface was attributed with the [InheritedExport] attribute, SearchPortal will know about the Bing class being a search engine implementation.

Listing 10-2.  The Bing Class Exposing Search Functionalities Through the Bing Search Engine

public class Bing
    : ISearchEngine
{
    public string Name
    {
        get { return "Bing"; }
    }

    public Uri GenerateSearchUri(string searchTerm)
    {
        return new Uri(string.Format(" http://www.bing.com/search?q={0} ", searchTerm));
    }
}
 

Importing Search Engines

In the previous section, we defined the ISearchEngine interface. This interface defined what a search engine plug-in should look like and how MEF can discover implementations of this interface.

MEF will look for implementations of the ISearchEngine interface in a so-called catalog of components. This catalog is created once when the application starts and is exposed through a CompositionContainer. The catalog and container will be created just once, at application startup. It is an expensive operation in terms of performance, and you are advised to do this only once, when explicitly needed.

The initialization code will be added to the Application_Start() method of the Global.asax file so it runs only at application startup. This code does all the MEF wiring in the SearchPortal application. A trimmed-down version of it is shown in Listing 10-3. If you want the full source code for this class, see the related source code download for this book.

Listing 10-3.  Global.asax Application_Start() Method

namespace SearchPortal.Web
{
    public class MvcApplication : System.Web.HttpApplication
    {
        public static CompositionContainer Container { get; private set; }

        protected void Application_Start()
        {
            // Load plugins
            ShadowCopyPlugins();

            var catalog = new AggregateCatalog(
                new AssemblyCatalog(typeof(MvcApplication).Assembly),
                new RecursiveDirectoryCatalog(HostingEnvironment.MapPath("∼/App_Data/ShadowedPlugins")));
            Container = new CompositionContainer(catalog);
            Container.ComposeParts(this);

            // ...
        }

        // ...
}
 

In the Application_Start() method, a call to the ShadowCopy() method is done. We’ll have a look at that method later in this chapter. Next, an AggregateCatalog is created, in which MEF will look for plug-ins. This AggregateCatalog is an aggregate of two other catalogs, an AssemblyCatalog that scans for plug-ins in the application’s assembly (perhaps you want to ship some search engines out of the box) and a RecursiveDirectoryCatalog that scans the ∼/App_Data/ShadowedPlugins folder and automatically registers the plug-ins that are installed through NuGet.

From now on, we can easily retrieve a list of all ISearchEngine implementations that are found by MEF. For example, the HomeController for SearchPortal can query the MEF container for all ISearchEngine implementations in its constructor, which is illustrated in Listing 10-4. The resulting IEnumerable will contain all ISearchEngine implementations discovered by MEF, if there are any, and can be used by the HomeController to decide which search engines should be shown in the drop-down list on the home page. The bold sections in Listing 10-4 illustrate using this IEnumerable. On a side note, we are querying the MEF container here directly. Many Inversion-of-Control (IoC) containers have support for automatically injecting the ISearchEngine implementations whenever the controller is instantiated.

Listing 10-4.  The HomeContoller Querying the Container for all ISearchEngine Implementations

public class HomeController : Controller
{
    private readonly IEnumerable<ISearchEngine> _searchEngines;
    public HomeController()
    {
        _searchEngines = MvcApplication.Container
            .GetExportedValues<ISearchEngine>();    }

    public ActionResult Index()
    {
        var viewModel = new SearchFormViewModel();
        viewModel.SearchTerm = "";
        viewModel.SearchEngine = _searchEngines.Select(e => e.Name).FirstOrDefault();
        viewModel.AvailableSearchEngines = _searchEngines.Select(
            e => new SelectListItem()
                        {
                            Text = e.Name,
                            Value = e.Name
                        }).ToList();

        return View(viewModel);
    }

    [HttpPost, ActionName("Index")]
    public ActionResult Index_Post(SearchFormViewModel model)
    {
        if (ModelState.IsValid)
        {
            var searchEngine = _searchEngines.FirstOrDefault(
                e => e.Name == model.SearchEngine);
            if (searchEngine != null)
            {
                return Redirect(
                    searchEngine.GenerateSearchUri(model.SearchTerm).ToString());
            }
        }

        return View(model);
    }
}
 

Installing Plug-ins into SearchPortal

SearchPortal features an administration area that enables administrators to install a search engine plug-in from a list of available search engines that can be installed or uninstalled. Using this page, the administrator picks the search engines to show in the drop-down list on the SearchPortal home page.

The administration area of the application is located at /administration. If you are running the sample code, administrator is the username and password is the password. This administrative interface has been developed as a dedicated ASP.NET MVC controller, the AdministrationController.

Listing 10-5 shows the source code for the AdministrationController class. The AdministrationController class consists of three action methods:

  • Index: This method queries an IPluginManager interface for a list of available and installed plug-ins. The IPluginManager interface and its implementation will be covered later in this section.
  • Install: This action method instructs the IPluginManager implementation to install a specific plug-in into the SearchPortal application.
  • Uninstall: This action method instructs the IPluginManager implementation to uninstall a specific plug-in from the SearchPortal application.

Listing 10-5.  The AdministrationController Enabling the Administrator of SearchPortal to List, Install, and Uninstall Search Engine Plug-ins

[Authorize]
public class AdministrationController : Controller
{
    private readonly PluginManager _pluginManager;

    public AdministrationController()
    {
        _pluginManager = new PluginManager();
    }

    public ActionResult Index()
    {
        var plugins = _pluginManager.ListPlugins();

        var viewModel = new AdministrationViewModel();
        viewModel.Plugins = plugins
            .Select(p => new PluginViewModel()
            {
                PackageId = p.PackageId,
                PackageVersion = p.PackageVersion,
                PackageDescription = p.PackageDescription,
                IsInstalled = p.IsInstalled
            })
            .ToList();

        return View(viewModel);
    }

    public ActionResult Install(string packageId, string packageVersion)
    {
        _pluginManager.Install(packageId, packageVersion);

        return RedirectToAction("Index");
    }

    public ActionResult Uninstall(string packageId, string packageVersion)
    {
        _pluginManager.Uninstall(packageId, packageVersion);

        return RedirectToAction("Index");
    }
}
 

The IPluginManager interface is a simple interface that enables its consumers to ListPlugins and to Install or Uninstall a plug-in. Listing 10-6 is the source code for this interface, which serves as an abstraction used by the AdministrationController.

Listing 10-6.  The IPluginManager Interface Used by the AdministrationController

public interface IPluginManager
{
    IEnumerable<PluginModel> ListPlugins();
    void Install(string packageId, string packageVersion);
    void Uninstall(string packageId, string packageVersion);
}
 

Of course, no NuGet magic will happen if we don’t implement the IPluginManager interface. This implementation will do the actual integration with the NuGet.Core namespace. The NuGet.Core assembly can be installed through NuGet itself. Simply use Install-Package NuGet.Core.

Among other classes, NuGet.Core features two interesting types:

  • The IPackageRepository interface is used to query a NuGet feed for information.
  • The PackageManager class manages installing, updating, and uninstalling packages to a local folder based on an IPackageRepository instance to retrieve a list of packages.

We’ll use both of these types in the constructor for our PluginManager class:

  
public class PluginManager
    : IPluginManager
{
    private readonly string _pluginFolder;
    private readonly IPackageRepository _packageRepository;
    private readonly PackageManager _packageManager;

    public PluginManager()
    {
        _pluginFolder = HostingEnvironment.MapPath("∼/App_Data/Plugins");
        _packageRepository = PackageRepositoryFactory.Default
            .CreateRepository(" https://www.myget.org/F/pronuget/ ");
        _packageManager = new PackageManager(_packageRepository, _pluginFolder);
    }
}
 

First of all, a plug-in folder is determined. This folder will hold all installed search engine plug-ins. We’ve chosen the /App_Data/Plugins folder for this. We’re using the HostingEnvironment class’s MapPath() method to determine the absolute path to this folder.

Next, a package repository reference is created. NuGet.Core features a default PackageRepositoryFactory, which can create a repository by simply passing the URL of the feed to consume. We’re passing in www.myget.org/F/pronuget, because this is the URL to the NuGet feed that holds search engine plug-ins.

Finally, a PackageManager instance is created, linking the package repository to a local folder where installed packages will be stored. In essence, this is the inner working of all NuGet components such as the NuGet command line and the Package Manager Console.

We still have some methods to implement. Let’s start with the ListPlugins() method:

  
public IList<PluginModel> ListPlugins()
{

    return _packageManager.SourceRepository.GetPackages()
        .Where(p => p.Tags.ToLower().Contains("searchportalplugin"))
        .OrderBy(p => p.Id)
        .Select(p => new { Id = p.Id, Version = p.Version, Description = p.Description})
        .ToList()
        .Select(p => new PluginModel()
            {
                PackageId = p.Id,
                PackageVersion = p.Version.ToString(),
                PackageDescription = p.Description,
                IsInstalled = _packageManager.LocalRepository.Exists(p.Id, p.Version)
            })
        .ToList();
}
 

Using the PackageManager created in the constructor of our PluginManager class, we query the source repository for all packages having the tag "searchportalplugin". Of course, other queries are possible here as well. You can search any property a NuGet package has in this query: you can look for packages having a specific ID, containing a search string in their title or summary, a specific package version, and so on. Be as creative as you want here.

The ListPlugins() method returns an IList of PluginModel instances. The PluginModel class is a simplified representation of a NuGet package that will be used by the AdministrationController mentioned earlier in this section. The PluginModel class contains only the package ID, the package version, the description, and a Boolean that tells SearchPortal whether the package is installed.

To check whether a package is installed, call PackageManager’s local repository method Exists.

Finally, the PluginManager should be able to install and uninstall NuGet packages. The Install method consists of only two lines of code:

  
public void Install(string packageId, string packageVersion)
{
    _packageManager.InstallPackage(packageId, new SemanticVersion(packageVersion));

    HostingEnvironment.InitiateShutdown();
}
 

As you can see, the Install method calls into the PackageManager’s Install method with the package ID and package version to install. From then on, the PackageManager will take care of downloading the package from the NuGet feed at www.myget.org/F/pronuget and extracting the package to the /App_Data/Plugins folder.

The second line of code in the Install method is interesting and is required only when working with web applications. The HostingEnvironment.InitiateShutdown() method requests the web server—for example, Internet Information Server (IIS)—to shut the running down instance of SearchPortal and to restart the application pool in which SearchPortal is running. We have two good reasons for doing this:

  • The MEF catalog containing all available plug-ins is created only once, at startup of the SearchPortal application. This catalog is rebuilt when the application is restarted, and only then is our newly installed search engine plug-in discovered.
  • The Common Language Runtime (CLR), .NET’s runtime, locks the assemblies loaded at runtime. This means that if we install a search engine plug-in, the CLR will lock the assembly containing the search engine plug-in. We will never be able to remove the assembly again, because the file will be in use by the CLR. In addition to restarting the application pool, we’ll be using a technique called shadow copying, which will be explained in the next section.

The complete source code for the PluginManager class which can list, install and uninstall packages from a specific NuGet feed is in Listing 10-7.

Listing 10-7.  The PluginManager Class

public class PluginManager
    : IPluginManager
{
    private readonly string _pluginFolder;
    private readonly IPackageRepository _packageRepository;
    private readonly PackageManager _packageManager;

    public PluginManager()
    {
        _pluginFolder = HostingEnvironment.MapPath("∼/App_Data/Plugins");
        _packageRepository = PackageRepositoryFactory.Default
            .CreateRepository(" https://www.myget.org/F/pronuget/ ");
        _packageManager = new PackageManager(_packageRepository, _pluginFolder);
    }

    public IList<PluginModel> ListPlugins()
    {
        return _packageManager.SourceRepository.GetPackages()
            .Where(p => p.Tags.ToLower().Contains("searchportalplugin"))
            .OrderBy(p => p.Id)
            .Select(p => new { Id = p.Id, Version = p.Version, Description = p.Description})
            .ToList()
            .Select(p => new PluginModel()
                {
                    PackageId = p.Id,
                    PackageVersion = p.Version.ToString(),
                    PackageDescription = p.Description,
                    IsInstalled = _packageManager.LocalRepository.Exists(p.Id, p.Version)
                })
            .ToList();

    }

    public void Install(string packageId, string packageVersion)
    {
        _packageManager.InstallPackage(packageId, new SemanticVersion(packageVersion));    }

    public void Uninstall(string packageId, string packageVersion)
    {
        _packageManager.UninstallPackage(packageId, new SemanticVersion(packageVersion));

        HostingEnvironment.InitiateShutdown();
    }
}
 

Loading Installed Plug-ins into MEF’s Catalog

So far, we’ve downloaded search engine plug-ins from a NuGet feed and installed them into the ∼/App_Data/Plugins folder. Now, it’s time to make sure they can be loaded by our application.

As mentioned earlier, Application_Start() creates a catalog containing possible plug-ins. This catalog uses a RecursiveDirectoryCatalog to load plug-ins from disk. If you’ve watched carefully, there is a mismatch in the directories we have been using: plug-ins are installed into the ∼/App_Start/Plugins folder, while MEF loads them from the ∼/App_Data/ShadowedPlugins folder. Listing 10-8 shows the relevant lines of code again.

Listing 10-8.  Adding the Installed Search Engine Plug-ins to MEF’s Catalog

// Load plugins
ShadowCopyPlugins();

var catalog = new AggregateCatalog(
    new AssemblyCatalog(typeof(MvcApplication).Assembly),
    new RecursiveDirectoryCatalog(HostingEnvironment.MapPath("∼/App_Data/ShadowedPlugins")));
Container = new CompositionContainer(catalog);
Container.ComposeParts(this);
 

There is a good reason for adding the ∼/App_Start/ShadowedPlugins folder to the catalog instead of the ∼/App_Start/Plugins folder, where the PluginManager installs its packages. The CLR locks the assemblies loaded at runtime. This means that all assemblies in the ∼/App_Start/Plugins folder would be locked, and we would be unable to uninstall packages once installed.

To overcome this problem, we’ll use a technique called shadow copying, which is also used by Microsoft to make it possible to compile ASP.NET web pages at runtime without being affected by CLR locks. Shadow copying may sound difficult, but it isn’t. It consists of creating a copy of the original assemblies and using that copy instead of the original assemblies. Doing this allows us to modify the original set of assemblies by, for example, installing or uninstalling a search engine plug-in. The shadow copy is then updated whenever the application pool is restarted.

Listing 10-9 shows a simple technique to create a shadow copy of the ∼/App_Data_Plugins folder to a folder called ∼/App_Data/ShadowedPlugins.

Listing 10-9.  Creating a Shadow Copy of the /App_Data/Plugins Folder to Overcome CLR Locks

private static void ShadowCopyPlugins()
{
    var shadowedPlugins = new DirectoryInfo(
        HostingEnvironment.MapPath("∼/App_Data/ShadowedPlugins"));

    // Remove old shadow copies
    if (shadowedPlugins.Exists)
    {
        shadowedPlugins.Delete(true);
    }
    shadowedPlugins.Create();

    // Shadow copy plugins (avoid the CLR locking DLLs)
    var plugins = new DirectoryInfo(HostingEnvironment.MapPath("∼/App_Data/Plugins"));
    if (plugins.Exists)
    {
        plugins.Copy(shadowedPlugins);
    }
}
 

Creating and Publishing Plug-ins

Until now, we’ve discussed only making the SearchPortal application pluggable. To make use of it, we’ll have to create some search engine plug-ins. You can do this by creating a new class library project in Visual Studio. Name the project SearchPortal.Plugins.SearchEngines.Google.

To ensure that the ISearchEngine interface is known by our plug-in, add a reference to the SearchPortal.Web project, which you can find in the downloads for this book. This project contains the ISearchEngine interface that we’ll use to implement a Google search engine plug-in for SearchPortal.

Add a class named Google to the project, and add the code in Listing 10-10 to it.

Listing 10-10.  Source Code for the Google Search Engine Plug-in

using System;
using SearchPortal.Web.Code.Contracts;

namespace SearchPortal.Plugins.SearchEngines.Google
{
    public class Google
        : ISearchEngine
    {
        public string Name
        {
            get { return "Google"; }
        }

        public Uri GenerateSearchUri(string searchTerm)
        {
            return new Uri(string.Format(" http://www.google.com/search?q={0} ", searchTerm));
        }
    }
}
 

To publish this plug-in to a NuGet feed, we’ll have to package it. Open a command prompt in the root folder of the newly created project SearchPortal.Plugins.SearchEngines.Google, and run the nuget spec command. This will create a file named SearchPortal.Plugins.SearchEngines.Google.nuspec that contains a package manifest for the Google search engine plug-in we are creating. Listing 10-11 lists the markup for the package manifest that will be used to create a NuGet package for this plug-in. Note that the tags element contains a tag named searchportalplugin; this is the tag we’ve specified in the PluginManager, because we want to show only packages having this tag in the SearchPortal application.

Listing 10-11.  The Package Manifest Used to Package the Google Search Engine Plug-in

<?xml version="1.0"?>
<package >
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <title>$title$</title>
    <authors>Maarten Balliauw, Xavier Decoster</authors>
    <owners>Maarten Balliauw, Xavier Decoster</owners>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>$description$</description>
    <copyright>Copyright Maarten Balliauw and Xavier Decoster 2013</copyright>
    <tags>pronuget apress plugin searchportalplugin</tags>
  </metadata>
</package>
 

From now on, you can use the knowledge you have gained from this book to package your project. Run the nuget pack command to create a package from the Google plug-in project, and publish this package to a NuGet feed of choice. If you have an account on MyGet.org, you can also push to the www.myget.org/F/pronuget feed by using your MyGet API key.

image Tip  As an alternative to manually running the nuget pack command, you can also Install-Package NuGet.Build and edit the NuGet.targets file’s <BuildPackage> element value to true. This will change the project file for the SearchPortal.Plugins.SearchEngines.Google project and will trigger the nuget pack command automatically on every build.

Creating a Self-Updating Application by Using NuGet

In addition to distributing plug-ins, why not distribute your application itself through NuGet? In fact, the NuGet command line is nothing more than a small bootstrapper downloading the NuGet.CommandLine package from NuGet.org on first run. The nuget update -self command does the exact same thing and updates the NuGet.CommandLine package to its latest version. It’s a good example to take a closer look at.

The NuGet Command Line as Example

In this section, we’ll use the NuGet source code that you can find at https://git01.codeplex.com/nuget. You can clone this URL by using Git. To clone the NuGet repository, simply run the following command, using the git executable in a command prompt, while making sure you are in the directory into which you want to clone the remote repository.

  
git clone https://git01.codeplex.com/nuget
 

This command will work only if you have a Git client installed. If not, use Chocolatey (described in Chapter 7), and invoke the cinst git command. If you don’t want to install a Git client, you can also navigate to the www.codeplex.com/nuget URL in your browser and browse through the NuGet source code online.

Let’s look at the nuget update command. The source code for this command is in the NuGet repository at /src/CommandLine/Commands/UpdateCommand.cs. This class contains a method named SelfUpdate, for which you can see the source code in Listing 10-12.

Listing 10-12.  Source Code for NuGet’s UpdateCommand SelfUpdate Method

private const string NuGetCommandLinePackageId = "NuGet.CommandLine";
private const string NuGetExe = "NuGet.exe";
private const string PackagesFolder = "packages";

internal void SelfUpdate(string exePath, SemanticVersion version)
{
    Console.WriteLine(NuGetResources.UpdateCommandCheckingForUpdates,
        NuGetConstants.DefaultFeedUrl);

    // Get the nuget command line package from the specified repository
    IPackageRepository packageRepository =
        RepositoryFactory.CreateRepository(NuGetConstants.DefaultFeedUrl);

    IPackage package = packageRepository.FindPackage(NuGetCommandLinePackageId);

    // We didn't find it so complain
    if (package == null)
    {
        throw new CommandLineException(NuGetResources.UpdateCommandUnableToFindPackage,
            NuGetCommandLinePackageId);
    }

    Console.WriteLine(NuGetResources.UpdateCommandCurrentlyRunningNuGetExe, version);

    // Check to see if an update is needed
    if (version >= package.Version)
    {
        Console.WriteLine(NuGetResources.UpdateCommandNuGetUpToDate);
    }
    else
    {
        Console.WriteLine(NuGetResources.UpdateCommandUpdatingNuGet, package.Version);

        // Get NuGet.exe file from the package
        IPackageFile file = package.GetFiles().FirstOrDefault(f =>
            Path.GetFileName(f.Path).Equals(NuGetExe, StringComparison.OrdinalIgnoreCase));

        // Get the exe path and move it to a temp file (NuGet.exe.old)
        // so we can replace the running exe with the bits we got
        // from the package repository
        string renamedPath = exePath + ".old";
        Move(exePath, renamedPath);

        // Update the file
        UpdateFile(exePath, file);

        Console.WriteLine(NuGetResources.UpdateCommandUpdateSuccessful);
    }
}
 

The SelfUpdate method contains a simple code flow that does the following:

  1. Get the NuGet.CommandLine package from NuGet.org.
  2. Compare the current version and the new version of this package to verify whether an update is required.
  3. Rename the current nuget.exe to nuget.exe.old.
  4. Extract the package (in memory), and extract the nuget.exe file from it to the current path.

These four simple steps are all it takes to create a self-updating application using NuGet.

An Application Bootstrapper That Checks for Updates

Now that we know how NuGet updates itself, we can also create our own application bootstrapper that checks for updates. Since the update check and installation are fairly common, we can abstract this into a stand-alone executable that checks for updates for your custom application.

Imagine we have an application called Bootstrapper.SampleApp that we package as a NuGet package. The executable and all its libraries are packaged into the lib folder of the NuGet package. Figure 10-6 shows what the contents of this package may look like.

9781430260011_Fig10-06.jpg

Figure 10-6.A NuGet package containing an executable

When distributing this application to our users, we don’t distribute the actual Bootstrapper.Sample.exe executable, but we distribute a simple bootstrapper. This simple bootstrapper will download the package from a NuGet feed if it wasn’t downloaded yet, and update it if a newer version exists. Figure 10-7 shows the output of our bootstrapper executable when a newer version of our application package exists.

9781430260011_Fig10-07.jpg

Figure 10-7.Updating an application from a NuGet feed

We can create this bootstrapper executable fairly quickly by going through the following steps:

  1. Check whether the application has been installed before. If not, use NuGet.Core to install it.
  2. If the application exists in disk, check the feed for a newer version. If a newer version is available, proceed with an update.
  3. Launch the executable that was wrapped in a NuGet package.

For all of these steps, the NuGet.Core assembly provides helper functions. Listing 10-13 shows how we can create a simple bootstrapper executing these steps. Lines in bold represent the actions performed against the NuGet feed.

Listing 10-13.  An Application Bootstrapper for Applications That Checks for Updates on Startup

private const string ApplicationFeedUrl = " https://www.myget.org/F/pronuget/ ";
private const string ApplicationPackageName = "Bootstrapper.SampleApp";
private const string ApplicationFolder = "application";
private const string ApplicationExecutableName = "Bootstrapper.SampleApp.exe";

static void Main(string[] args)
{
    var packagesFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ApplicationFolder);
    var packageRepository = PackageRepositoryFactory.Default
        .CreateRepository(ApplicationFeedUrl);
    var packageManager = new PackageManager(packageRepository, packagesFolder);
    var versionFile = Path.Combine(ApplicationFolder, "version.txt");

    if (!Directory.Exists(packagesFolder) || !Directory.EnumerateDirectories(        packagesFolder, ApplicationPackageName + "*", SearchOption.AllDirectories).Any())
    {
        Console.WriteLine("Installing {0}...", ApplicationExecutableName);
        IPackage package = packageRepository.FindPackage(ApplicationPackageName);
        File.WriteAllText(versionFile, package.Version.ToString());
        packageManager.InstallPackage(package, true, false);
    }
    else
    {
        Console.WriteLine("Checking for {0} updates...", ApplicationExecutableName);

        var currentVersion = new SemanticVersion(File.ReadAllText(versionFile));
        IPackage package = packageRepository.FindPackage(ApplicationPackageName);
        if (package.Version > currentVersion)
        {
            Console.WriteLine("Updating to version {0}...", package.Version);
            packageManager.UpdatePackage(package, false, false);
            File.WriteAllText(versionFile, package.Version.ToString());
        }
        else
        {
            Console.WriteLine("No updates were found.");
        }
    }

    // Find executable
    var executablePath = Directory.EnumerateFiles(packagesFolder,
        ApplicationExecutableName, SearchOption.AllDirectories).FirstOrDefault();
    if (!string.IsNullOrEmpty(executablePath))
    {
        Console.WriteLine("Starting {0}...", ApplicationExecutableName);
        Process.Start(executablePath);
    }
    else
    {
        Console.WriteLine("Could not find {0} executable.",
            ApplicationExecutableName);
    }
}
 

The code from Listing 10-13 can be used in your applications as well to provide an auto-update functionality based on NuGet.

Summary

In this chapter, we’ve changed our perspective and looked at NuGet as a protocol for distributing packages. We’ve leveraged techniques used by all NuGet components: using its central assembly, NuGet.Core, we have created a plug-in system for an ASP.NET MVC application that makes use of NuGet’s features for working with feeds (or repositories) and installing and uninstalling packages based on such feeds.

This technique is also used by NuGet Package Explorer, as you saw in Chapter 9. Plug-ins for NuGet Package Explorer are distributed through a NuGet feed, to which you can contribute your NuGet plug-ins.

You’ve also seen how the NuGet command-line tool is, in essence, also using this technique: when invoking the nuget update -self command from the command line, NuGet updates itself by querying NuGet.org for the NuGet.CommandLine package.

If you are working with any plug-ins that users can install into your application or want to work with a self-updating executable (much like nuget.exe update -self), the techniques described in this chapter will be of use to you. It’s up to you to find a creative use case for these techniques.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.148.117.212