Sunday, September 8, 2013

Resharper Extension - Auto Nuget Installation

In this post we are going to discuss a new Resharper extension. We are going to discuss why we need it and how it makes our lives easier as developers. The extension is called Nuget Support for Resharper. The package works like another feature of resharper where it suggests assembly references for types used in a project based on the other projects in a solution. The extension extends support for suggesting nuget package reference instead of an assembly reference if the assembly comes from a package. You can install the package using Resharper 8 EAP Extension Manager.

We discussed about Extension Manager feature in Resharper 8 EAP before. You can refresh your knowledge here.

Creating Package
Let's create a sample class library project Core.Interfaces. The project is targeting to framework version 4.5. You can skip this part if you already have a package to reference.

Let's add some interfaces to the project. This is a library for providing necessary API for order processing. Here we are providing an interface IOrder as a contract for an order. We are also adding an interface for Order Factory.

Let's first create the nuspec file for the project. We use Nuget command line tool to create the specification file. We need to provide the path of command line tool so that it could create nuspec for the *.csproj file in the current folder. To save the trouble each time, you can provide the tool's path in the %PATH% environment variable.

The specification is created with a few tokens. Let's cleanup the file keeping the required tokens as follows:

The tokens are populated with the assembly attributes as specified in AssemblyInfo.cs in the project if this nuspec is used to create the project. You can use the following mapping from specification.

For our project, I have updated the contents of AssemblyInfo.cs as follows:

Now let's update the post-build event for the project to create a nuget package. Since we have nuspec file in the same directory as the csproj file, nuget tool should use nuspec file definition to create the package. It should then use the assembly for token replacement generating the resulting package. Here we are creating the package in a specific output directory. For the local development, we can use the path as a local repository for the package. Now updating the package in any project would use the latest source package.

Now whenever we build the project, the nuget package is created in the output directory. The package for Core.Interfaces is created as follows:

Adding Local Package Repository
Now let's make sure that the package is used from the local storage. We can also uncheck path to make sure that packages from only this repository are being used. You might need to clear up local package cache for extra safety. Let's add the local package path to the nuget package manager setting as follows:

Using Package from Local Repository
Now let us add another class library project Core.OrderProcessing. We install the nuget package previously created to this project. Just make sure that Local Repository is selected before you install the package.

Let's provide the definition of the interfaces introduced in Core.Interfaces project as follows:

Now let's add another project to write unit tests for our library. Let's use xUnit for these tests.We discussed about this extension in our Resharper 8 features' discussion here.

Let's add the following tests for testing OrderFactory:

Since the types are returning interface types, we need to reference Core.Interfaces library. Here Resharper is really helpful for helping us what library references we are missing. It goes one step further by making it easier. It provides the support in action bar to add the required assembly, which is quite amazing.

Selecting the above option add the required assembly reference in the project. This is a well known resharper's feature. Although this is great for other assemblies but this is not perfect for the assemblies coming from a nuget package, which is exactly the case here. The assembly is referenced from packages folder.

Extension to Install the Source Package
In order to help us with the above scenario, we can use Nuget Support for Resharper. This is a resharper extension which can be installed using Resharper's Extension Manager. If we install this, then selecting the same extension would not directly reference the assembly from packages folder. It would install the source package instead. Installing the source package would automatically install the other dependencies for this package, which is even better.

It must be remembered that the extension doesn't add an extra item to the action bar. It just uses the option to add the assembly reference but it installs the source package, and hence, all of it dependencies. Since the type was sourced from Core.Interfaces, the same package is installed. An entry is added to project's packages.config file. The package installs a reference to the required assembly. Additionally all the required namespaces are added to the code.


Possible Improvements
There is still room for improvement for the extension. Although it does a very good job for installing the source package in the scenario where the types are sourced from a package but it seems to only work in the scenario where there is an explicit usage of types. In the case of implicit scenario where the assembly should be referenced because of cases like return type of a method used. In those cases, still the resharper suggests to add the required assembly and the assembly is directly referenced from the packages folder. Let's update the code for our test as follow.

Here we are not explicitly specifying the type but since GetOrder returns an IOrder, it requires an assembly reference of Core.Interfaces.

Let's see the reduced options in the action bar. There is an option to add the required assembly reference. Let's use that.

Selecting the above does add the required assembly reference (resharper's feature) from packages folder. It doesn't install the required package. I think the extension can also listen for this scenario and install the source package instead.


No comments: