NuGet logo

Nuget Package restore issues

After upgrading Nuget on VS2013, we’ve started hitting an issue where Nuget won’t restore packages. We get error messages similar to this:

Error 21 Could not find a part of the path 'C:\Dev\YourProject\ \packages\Microsoft.AspNet.WebApi.Core.5.2.3'.

I’ve found out that there is a change to the way Nuget picks up files. To get it to work you need to edit the file NuGet.targets changing the line:

<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>

To:

<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(SolutionDir)\</RestoreCommand>

This sorts the issue.

Advertisements

Adding Swagger to a C# WebApi

About Swagger

Swagger (https://swagger.io/) is a free open-source tool that enables easy documentation and testing of APIs. It is language agnostic.

Swashbuckle (https://github.com/domaindrivendev/Swashbuckle) is a .Net specific tool that adds Swagger to a .Net Web Api.

Installation

Install from Nuget

Install-Package Swashbuckle

Register Swagger

The Swashbuckle install should create a SwaggerConfig.cs file in the App_start Directory. This will allow you to configure Swagger in various ways.

Enable XML document generation

Open the project properties for your WebApi project, select the build tab and change the setting below:

Annotation 2019-02-20 111835

Update swagger config to use the XML comments

In the SwaggerConfig.cs file uncomment the line below:

c.IncludeXmlComments(GetXmlCommentsPath());

and add the method :

private static string GetXmlCommentsPath()
{
   var filepath = System.String.Format(@"{0}\bin\documentation.XML", System.AppDomain.CurrentDomain.BaseDirectory);
   return filepath;
}

Adding XML comments to code

On your controller and individual endpoints add the /// triple slash comments, for example

///
/// Search returns a paged, filterd list of events
///

///filters for the search ///number of entries to return per page ///page number to return ///name of column to sort by ///direction of sort /// A list of Events /// Success /// No Data Found /// Internal Server Error [Route(“Search”)] [HttpGet] [SwaggerResponse(200)] [ResponseType(typeof(IQueryable))] public IHttpActionResult GetList([FromUri] EventArguments parameters, [FromUri] int pageSize = 25, [FromUri] int pageNumber = 1, [FromUri] string sortColumn = “Id”, [FromUri] bool sortForward = true) { … }

 

Creating a multi project solution in VS2013

Ok, so this is probably a bit out of date as VS2019 is due out soon, but one of my clients is still using VS2013 as their main IDE, as they can’t update one of their projects for various reasons.

The project I’m working on them with is an AngularJS front-end to a micro-services architecture with around 20 micro-services. Creating a new service can be a bit of a pain, especially keeping them in line with internal standards, therefore I created a multi-project template to create a new service, and it does it in around 5 minutes from scratch to a working WebApi service.

I followed the guide here: https://docs.microsoft.com/en-us/visualstudio/ide/how-to-create-multi-project-templates?view=vs-2017, but there are a couple of gotchas.

    1. Take a working solution and copy it into a new folder, remember to remove source control bindings.
      folder structure
    2. Strip the solution to a bare bones solution that provides a working example of what you want do do. For this project, I have one data model, one DTO, one repository & interface, one service and interface and one WebAPI controller with one endpoint.solution Explorer
    3. Check that it still runs.
    4. Clean the solution, delete contents of bin and obj directories  in each project.
    5. In the File menu choose Export Template, and export each project. These will be exported to a folder like C:\Users\XXX\Documents\Visual Studio 2013\My Exported Templates
      Export Template Menu Item
      Export Template Page 1

      Export Template Page 2
    6. Copy each of them into the respective project folders and rename them to match the project name.
      Project contents
    7. Create a root.vsTemplate file – Refer to the guide for more info on this.
    8. Select the project folder and the root.vsTemplate (exclude the solution file, packages folder and .nuget folder) and right click –> Send to –>Compressed (zip) file.
    9. Rename the zip file and copy it to C:\Users\XXX\Documents\Visual Studio 2013\Templates\ProjectTemplates.
    10. Open a new copy of Visual Studio and select File\Create a new project, search for your template and create a new project.
    11. You should have a fully working WebApi.
    12. You can now add the package to a VSIX project and make that available for everyone to use.

 

 

Setting up Development Configurations in Visual Studio

Working in a medium sized development team, we have found a source of frustration is when one developer checks in a change to the web.config that they’ve been using for testing that affects everyone else.

While looking at ways to stop this we discovered that you can have the web.config load settings in from other files.

ConfigSource

Some sections of the web.config allow you to add a ConfigSource attribute. for instance the connectionStrings section.  Before we would have several connectionStrings sections, one for each environment, but commented out.

Now, by adding the ConfigSource attribute, we can save the different connectionStrings sections to separate files, e.g. Dev, Dev2, etc., and simply change which one we point to. It’s much cleaner and more readable.

 <connectionStrings configSource="ConfigFiles\Dev_ConnectionStrings.config" />

AppSettings

AppSettings doesn’t allow you to use the ConfigSource, however, it does allow you to use the file attribute. The difference is that the file attribute brings in settings from another file that override the current settings.

<appSettings file="LocalSettings\DeveloperAppSettings.config">
   …
   …
</appSettings>

Also, the file is optional, so that if it doesn’t exist, the project will still run.

Example web.config extract

<connectionStrings configSource="ConfigFiles\DEV_ConnectionStrings.config" /> 
<appSettings file="LocalSettings\DeveloperAppSettings.config">
   <add key=”settingToOverride” value=”1”/> 
</appSettings>

ConnectionStrings.config

<connectionStrings>
  <add name="Database1Connection" connectionString="data source=Server1;initial catalog=Database1;user id=Database1User;password=password;" providerName="System.Data.SqlClient" />
  <add name="Database2Connection" connectionString="data source=Server2;initial catalog=Database2;user id=Database2User;password=password;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
</connectionStrings>

DeveloperAppSettings.Config

<appSettings>
   <add key=”settingToOverride” value=”2”/>
</appSettings>

Excluding the local files from TFS

First, the solution or project should have a “.tfignore” file created.

Example .tfignore file

###############################
# Ignore developer config files
\LocalSettings
# ignore logs
\logs
###############################
  • Check this file in.
  • Close and reopen the solution – This is important, as it seems to initialise the .tfIgnore.
  • Create the folder LocalSettings
  • Add the ConnectionStrings.config file
  • Add the DeveloperAppSettings.config file
    • Under Properties, set the Build Action to NONE
  • In Source Control Explorer, undo the changes to the DeveloperAppSettings.config file.

See also:

NuGet logo

Automatically building NuGet packages for your project–updated

As a follow on from my last post on this subject, I’ve come across a simplified way of doing this.

  1. Build your .nuspec file as usual. (use NuGet spec in a CMD window in the project folder to generate the basic file).
  2. Update the AssemblyInfo.cs file with a description and a company, or the NuGet build will throw errors.
  3. Edit the project file:

Add these two lines to the first <PropertyGroup>:

<RestorePackages>true</RestorePackages>
<BuildPackage>true</BuildPackage>

Add these lines at the end for the file:

<ImportProject=”$(SolutionDir)\.nuget\NuGet.targets”Condition=”Exists(‘$(SolutionDir)\.nuget\NuGet.targets’)”/><TargetName=”EnsureNuGetPackageBuildImports”BeforeTargets=”PrepareForBuild”>    <PropertyGroup>
      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
      </ErrorText>
   </PropertyGroup>
      <ErrorCondition=”!Exists(‘$(SolutionDir)\.nuget\NuGet.targets’)”Text=”$([System.String]::Format(‘$(ErrorText)’, ‘$(SolutionDir)\.nuget\NuGet.targets’))”/></Target>

SQL Server: Finding a column in multiple databases.

My client has many instances of the same database in various versions. The version I’m developing against is missing a field expected by a c# class, and I can’t find a script to create the column.

I came up with the following SQL Server query to allow me to search all the instances of the database to find one that contained the field.

EXEC sp_MSforeachdb 
 'SELECT ''Found in ?''
 FROM sys.columns 
 WHERE Name = N''FieldName''
 AND Object_ID = Object_ID(N''TableName'')'

Replace the field name and the table name with the values you’re looking for and it will search all the databases on the server for  a match.

Launching .EXE applications from a browser

My client has an Angular intranet portal for their main c# WinForm application. They have a requirement to launch the main application from the portal website.

There is a fairly easy way to go about this in Windows although it does require adding an entry to the registry, to set up a URI Scheme. Here is an example to load notepad.exe

HKEY_CLASSES_ROOT
   jmNotepad
      (Default) = "URL:jmNotepad Protocol"
      URL Protocol = ""
      DefaultIcon
         (Default) = "notepad.exe,1"
      shell
         open
            command
               (Default) = "C:\windows\notepad.exe"

In the web page, add a simple link:

<a href="jmNotepad:">Launch Notepad</a>

The first time it’s launched you get a warning, which you can click to ignore subsequently.

Trouble with Keys

I’ve just spent a couple of hours scratching my head with database/entity framework problem.

My client has a requirement to duplicate records from one database to another when the first is updated.

The tables were identical and based on a class inherited from the IdentityUserRole class, with another field added to the composite key.  However, although data saved OK into the original database, an error was being thrown when trying to duplicate the records into the second database.

After some debugging, I found out that it was an issue with the primary key. It wasn’t recognising the composite key.

The quick fix was to add the following into the dbContext class, in the OnModelCreating method:

modelBuilder.Entity<ApplicationUserTeamRole>().ToTable("AspNetUserRoles")
 .HasKey(x=>new{x.RoleId,x.UserId, x.TeamId});

Everything immediately started working.

WebApi2, CORS & HTTP 401 Unauthorized error

I got caught out with this issue again today, and it took me ages to find/remember the fix. Whilst using VS2013 to debug a website & web service running on separate ports on localhost, I kept getting a 401 error. I checked and rechecked that I had CORS set up properly, but the error just wouldn’t go away.

At last I remembered to set the default credentials on the WebClient instance:

        public User GetUser(string userName)
        {
            User model = null;
            string json = string.Empty;
            using (WebClient client = new WebClient())
            {
                client.UseDefaultCredentials = true;

                try
                {
                    string url = this.Url + "/"  + HttpUtility.UrlEncode(userName);

                    json = client.DownloadString(url);
                }
                catch (WebException)
                {
                    json = string.Empty;
                }
            }
            if (json.Length > 0)
            {
                model = JsonConvert.DeserializeObject(json);
            }
            return model;
        }