Category Archives: development

Sharing NuGet repository across multiple solutions

I’ll start with be summary BenPhegan posted to a thread I started on NuGet CodePlex site.

  1. Developers are historically used to having a local common library location for all their binary dependencies (ie all hintpaths reference “d:\common” or something similar).
  2. Often this library is controlled centrally and xcopy/robocopied to all developers machines to provide commonality of build capability, generally the central copy come from a CI build system.
  3. There are a lot of different projects that a developer works on with a lot of dependencies, and it is seen as efficient to have a single copy of these dependencies on disk.
  4. Project files are included from multiple solutions at arbitrary locations on disk, resulting in mismatched relative hintpaths when using NuGet.

The problem (not the only one) with shared packages directory is that every project that ever loaded a NuGet package creates a record in repositories.config file. You end up with this file containing lots of these. Also some packages directories might get deleted when a package is removed.

First step: Solution-level repositories.config file

My first approach to an “enterprise” NuGet was to get rid of the repositories.config file that is located under packages directory. All the packages.config files can be discovered by iterating through all projects in the solution:

const string fileName = "packages.config";
foreach (var project in solutionManager.GetProjects())
{
  var projectItem = project.FindProjectItem(fileName);
  if(projectItem == null)
  { 
    continue;
  }

  // we have the packages.config file here
}

But then I thought about it and got the idea that I can make repositories.config file local to a solution, i.e. put it in solution scope. And the best place to put NuGet specific file is the .nuget directory. The entries in config file are added with path relative to solution folder.

Solution
  .nuget
     nuget.config
     nuget.exe
     nuget.targets
     repositories.config
  ProjectA
  ProjectB
  packages.config

Now there’s one repositories.config file for every solution containing the records for every available packages.config. Package files in shared directory can be still deleted when the package in repository is no longer needed (this feature can be turned off and shared repository must be cleared manually).

Vote for it on NuGet!

I tried to avoid big changes in NuGet codebase and I was slowly getting to understand how NuGet internals works. Next step was to force NuGet to use the shared packages repository. I changed some classes (get the settings from registry) to find out the only thing I have to do was to add a node to C:\Users\(your name)\AppData\Roaming\NuGet\NuGet.config configuration file. Don’t forget to remove any repositoryPath from a Nuget.config file under your solution directory if you have one.

<config>
  <add key="repositoryPath" value="path to your repository"/>
</config>

The branch and changset details can be viewed on CodePlex here.

The description of how to build (locally and CI) a solution with project(s) referencing assemblies in a shared local directory will follow.

Data-driven Knockout views II

Last time I posted a code snippet with description of data driven Knockout views. I have modified and simplified it and

  • added support for forEach binding and
  • the type member of ViewModel class can return a function that is evaluated for every item in collection

This brings even more flexibility, the view model itself can decide which view to use based on a value returned from type function.

define([
    'knockout'
], function(ko) {

    ko.bindingHandlers.content = {

        'init': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            var options = ko.utils.unwrapObservable(valueAccessor());

            var templateNameFunc = function(value, itemBindingContext)
            {
                if('template' in options)
                    return options['template'];

                if(!value['type'])
                {
                    throw new Error("Set 'type' of view model class.");
                }

                var type = value.type;
                if(typeof type == 'function')
                    type = type();

                return type.replace(/viewmodel/ig, 'View');
            };

            if ('foreach' in options) {
                var dataArray = (options['foreach']) || [];
                ko.renderTemplateForEach(templateNameFunc, dataArray, options, element, bindingContext);
                return;
            }

            var templateName = templateNameFunc(options.data),
                dataValue = ko.utils.unwrapObservable(options['data']);

            var innerBindingContext = bindingContext['createChildContext'](dataValue, options['as']);
            ko.renderTemplate(templateName || element, innerBindingContext, options, element);
        }
    };
});
contentBindingHandler.jsview rawview file on Bitbucket

The example will follow soon.

Data-driven Knockout views

I’m using Caliburn.Micro in my Silverlight and WPF projects. I fell in love with Caliburn automagic – view locator, auto binding, etc. For my NodeJS project I was looking for JavaScript library supporting MVVM pattern and I found KnockoutJS.

Knockout has very nice feature called binding with a simple way of implementing your custom binding.

What I was missing was Caliburn’s binding/convention for ContentControl – data driven view rendering:

<!-- DataContext is RootViewModel -->
<ContentControl Name="ChildViewModel" />

with the view located for value of ParentViewModel.ViewModel property and then rendered.

The solution I come with uses

  • Infuser as a templating engine, more on using Infuser as templating engine for Knockout here
  • TrafficCop used by Infuser and prevents duplicate AJAX requests

Infuser is configured to load the view templates from views directory (I will implement some view locator later):

infuser.defaults.templateSuffix = ".tmpl.html";
infuser.defaults.templateUrl = "/js/app/views/"; 

and every view model object must have a property returning its type name (if someone knows about better way how to do it, please let me know).

var SampleViewModel = kb.ViewModel.extend({
  constructor: function(model) {
    ...
    this.type = 'SampleViewModel';
  }, 
  ...
}

Code of the binding handler:

ko.bindingHandlers.content = {
  'init': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    var options = ko.utils.unwrapObservable(valueAccessor())
    var templateName = options.data.type.replace(/viewmodel/ig, 'View'),
      dataValue = ko.utils.unwrapObservable(options['data']);
    var innerBindingContext = bindingContext['createChildContext'](dataValue, options['as']);
    ko.renderTemplate(templateName || element, innerBindingContext, options, element);
  }
};

and the HTML

<div data-bind="content: { data: childViewModel }"></div>
<script>
  var rootViewModel = kb.ViewModel.extend({
    constructor: function(model, options) {
      this.childViewModel = new SampleViewModel();
    }
  });
  ko.applyBindings(rootViewModel);
</script>

NuGet pack error “Assembly outside lib folder”

When I was working on a NuGet package I noticed that some DLLs (that were not supposed to be placed there) were copied into the content subdirectory of the package.

Added file 'content\lib\Microsoft.Practices.EnterpriseLibrary.Common.dll'.

And that also caused an issue:

Issue: Assembly outside lib folder.
Description: The assembly 'content\lib\Microsoft.Practices.EnterpriseLibrary.Common.dll' is not inside the 'lib' folder and hence it won't be added as reference when the package is installed into a project.
Solution: Move it into the 'lib' folder if it should be referenced.

It took me some time to figure out that NuGet copies all project items with BuildType set to Content to content directory in package. Problem was solved by changing BuildType to None.

How I failed using MSTest on TeamCity without Visual Studio

I was setting up a continuous integration for one project. I have configured a build step in TeamCity to build the solution and another to execute unit tests using MSTest. And that failed – the reason was Visual Studio was not installed on that machine. I googled around and found some useful posts:

The key components for running tests are MSTest.exe and QTAgent.exe. So I copied all the files to desired locations, I didn’t added the DLLs to GAC, opened command line and executed MSTest.exe and …

… an exception! Could not load file or assembly xxx.dll

So I found that DLL on my computer and copied it to directory with MSTest.exe, then fired MSTest.exe and another assembly load exception and again and again.

Because I could I installed VS.NET 2012 on that machine just to avoid this eternal cycle.

For those who can’t I used Fusion Log Viewer (fuslogvw) to log all assembly bindings to disk and then analyzed the files in LinqPad to get the list of loaded DLLs and their locations. See the attached file (some of the binded DLLs might be required by the tested project).

Hope this will help someone.

Fix an error in PowerShell script in NuGet package without redeployment

Recently I created a NuGet package that has a little bit complicated install.ps1 script. The package did not add any references to project, but imported a .targets file.

The structure of the package is

MyPackage
  tools
    dir_with_libs
      my.targets
  content (empty)
  libs (empty)

During the installation the relative path to the my.targets file had to be resolved. This took me some time until I found the proper solution and I had to

  1. install the package
  2. see that an error occurred
  3. uninstall the package
  4. fix the error
  5. rebuild the package in another VS.NET window (unfortunately this package was loaded from NuGet feed published on TeamCity, when deploying the package locally to a directory that emulated NuGet feed it is much easier)
  6. repeat this until there’s no error

In case you don’t want to rebuild the package whenever you have fixed an error in PowerShell script, you have to have solution with 2 or more projects. You will have to install the package to 2 (or more) projects. When you spot the error just fix the install.ps1 file located in the directory where package was created (usually `packages\Package.version\tools\install.ps1′).

Then just reinstall the package in one of yours projects. Because the package is now used in 2 or more projects it is not uninstalled and its directory is not removed. when Uninstall-Package is invoked. On Install-Package NuGet finds out that it is already installed in packages directory and gets the files from there.

Note that this will work only for files in package that are used by NuGet package manager without redeploying, like PowerShell scripts, text files, etc.