It started as a quick evening project (HTML Pager for Bootstrap), one class library I needed for some web projects to generate Bootstrap-happy pager HTML, and it ended up in hours of chasing weird VS2015 project configuration issues. I just wanted to package Core project into nuget and use it from MVC6 Core projects and also "old" full .NET 4.6 web projects (MVC5). Soon after I started splitting projects and importing DLLs, Visual Studio complained and I just couldn't reference Core project from my MVC 5 web app!
Scott Hanselman explained similar in his blog post, but I needed the exact opposite thing!
Maybe .NET Core is in RTM, but it reminded me that tooling support is still in deep Preview 2 state where half of things just doesn't work, meaning there's no UI support for them.
Plan is to have 3 packages:
-
Core class library, with no special dependencies to MVC, but just pager renderer class (xproj)
-
MVC 5 html helper wrapper (full .net 4.6, csproj), dependency to MVC 5
-
MVC 6 html helper wrapper (core, netstandard1.6, xproj), dependency to MVC 6
For 3), its easy since xproj can be referenced from another xproj, but Core project can't be referenced from 4.6 (csproj) project! The trick is to package core bits into nuget and then import that into csproj.
Core class library needs to specify both frameworks in project.json, like this:
"frameworks": {
"netstandard1.3": {
"dependencies": {
"NETStandard.Library": "1.3.0"
}
},
"net46": {
"dependencies": { "System.Runtime": "4.0.20" }
}
}
It has two frameworks, so library can be used from .net 4.6 and Core. I'm using Func<,> and System.Runtime package has to be added to net46 project.
The problem is, you can Add Project Reference to it from other Core (xproj) project, but not from standard 4.6 project. To use it from 4.6, a package needs to be created and added through nuget. Every time I change something in the Core lib, I have to create the package and re-install it to 4.6 project.
In my case, I added small .bat file to the Core project root, which creates package into a common folder:
dotnet pack -o ../../InternalRepository
Dotnet pack command uses information from project.json to create the package, like id, title, dependency libs, etc. Convenient UI tool for managing nupkg and nuspec files is Package Manager Explorer https://github.com/NuGetPackageExplorer/NuGetPackageExplorer
InternaRepository is folder referenced by solution nuget.config, used by all projects in the solution:
<configuration>
<packageSources>
<add key="Local" value="InternalRepository" />
<add key="NugetV3" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>
Note: if you change something in Core project, package needs to be created again and re-installed into 4.6 project. One way is updating build semver (1.0.x) and then update the package, other way is re-installing existing package with (in Package-Manager Console):
Update-Package JA.Pagination -reinstall
Why I didn't just pack Core package into nuget and used that? I wanted to have extension methods for Razor, so pager can be called with just Html.RenderPager(...), instead of Html.Raw(Pager.Build(...).Render()).
Since MVC5 and MVC6 have different HtmlHelper classes, I needed two additional projects, each one targeting different version of MVC and adding extension method to specific HtmlHelper class.
To be honest, I’m still not sure if this is the best way of managing 4.6 and Core projects in the same solution, but it’s the only thing that worked! In case somebody knows better way, I would be happy to hear about it, so please post the comment!
Full source code is available on Github.