How to share assembly info in ASP.NET Core? - asp.net-core

I'm developing a REST Api using ASP.NET Core. I used to shared assembly info across my solution using a shared file containing common attributes such as AssemblyCompany, AssemblyCopyright, AssemblyTrademark and AssemblyVersion. In this way all of my projects in a solution would be compiled with the same Assembly info attributes.
In ASP.NET Core these attributes are now defined in the project.json file. Is there a way to share these attributes in a similar way?

In case others get by here and are looking for a solution that works with .Net Core > 2.1 and the new .csproj project files:
After some research I got it working using Directory.Build.props file in a parent directory. You can find the sample code and some more information in this repository.

With the exception of AssemblyVersion, the assembly attributes you mentioned can still be defined in a shared file. This file can be added to the project under "buildOptions".
Example:
{
"buildOptions": {
"compile": "../shared/AssemblyInfo.cs"
}
}
See https://learn.microsoft.com/en-us/dotnet/articles/core/tools/project-json#buildoptions for more details.
AssemblyVersion can't be shared because it is always generated from the "version" value in project.json. "version" defaults to 1.0.0 if not specified.
Other attributes, such as AssemblyCopyright, will not be generated if their corresponding field is left empty in project.json.

Related

(System.IO.FileNotFoundException) .Net core Web API cannot find child dependency (Dependencies of dependency) when added DLL by "Add Reference"

I am getting System.IO.FileNotFoundException in my .Net Core Web API. So I've set up the below project to demonstrate the problem.
I created a.Net Standard library named DemoLibrary and added QRCoder dependency via NuGet.
Disclaimer: Reason for choosing the QRCoder is that the Web API doesn't use it by default. I don't use it in my project. In fact, I'm getting this exception for EntityFrameworkCore.
I created a new .Net Core Web API DemoWebAPI which has no other dependencies.
Then added the DemoLibrary to DemoWebAPI via Add Reference -> Browse -> DemoLibrary.dll.
This is my solution:
The DemoMethod method in Calculate class just creates the object of QRCodeGenerator.
public class Calculate
{
public static string DemoMethod()
{
QRCodeGenerator qrGenerator = new QRCodeGenerator();
return "";
}
}
And my ValuesController in DemoWebAPI just calls the method:
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2", DemoLibrary.Calculate.DemoMethod() };
}
Now, when I run the DemoWebAPI project I get below exception upon the call to the DemoMethod:
System.IO.FileNotFoundException: 'Could not load file or assembly 'QRCoder, Version=1.3.5.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.'
I understand the fact that I have to copy the QRCoder.dll file somewhere. But I fail to understand where to put it. I've already tried putting it in "bin/debug/netcoreapp2.2" of the DemoWebAPI and "bin/debug/netstandard2.0" of the DemoLibrary.
But I couldn't get it working.
Request: Please post your answer as descriptive as you can because I am new to .Net Core.
Edit:
I am aware of the NuGet servers. I have read topics like hosting a NuGet server in IIS and Azure. The reason behind DLL reference is I want to use my DLLs in two projects one of them is a .net core API and the other is .net framework class library which is compiled by NMAKE. I couldn't find any way to restore NuGet packages in the .MAK files.
It looks like you've merely added the DLL for DemoLibrary to your DemoWebApi project. That's not how you should be adding references. Since these are in the same solution, you should add a project reference. That will fix your issue.
Now, let me explain what's actually going on here. Your DemoLibrary has a dependency on QRCoder. It's a NuGet reference, which means that package will be restored (i.e. downloaded) and included in your DemoLibrary build output. However, it will be included as one or more DLLs along side your DemoLibrary.dll. When you then just reference DemoLibrary.dll, you're missing all these other DLLs that are part of DemoLibrary and thus, things don't work properly.
Now, when it comes to a project reference, things are little more complex. A project reference essentially wraps the referenced project into your other project. You can think of it as sort of a sub project. For all intents and purposes, it's like any dependency of the sub project becomes a dependency of the main project. That means that DemoWebAPI now technically has a NuGet package reference to QRCoder even though there's no explicit package reference in its project file. The dependency comes from your DemoLibary project. As such, with a project reference, all the necessary dependencies will be included, because it's as if the main project included those itself, by way of the sub project.
For what it's worth, you should virtually never include a DLL as a reference directly. That used to be required, but the concept of NuGet packages has all but eliminated the practice. Even if DemoLibrary was not in the same solution as DemoWebAPI (meaning you could no longer do a project reference), the correct way to use it would be to turn DemoLibary into a NuGet package, and then reference it in DemoWebAPI via a package reference, like any other NuGet package. You do not simply add the DLL.

How can I make a .NET Core class library and reference it from a .NET 4.6 project?

I want to:
Make a class library that defines some interfaces and simple generic helper classes. It'll rely on generic collections and IQueryable<T> but no third party dependencies (well, JetBrains.Annotations).
Be able to reference that class library from everywhere (specifically UWP, net46 and ASP.Net Core RC2)
Ideally, use the project.json system throughout, although I'm prepared to sacrifice that if need be.
Publish the finished library to a NuGet feed and from there use it in other apps
When creating my class library project in Visual Studio 2015.2, I found the Class Library (.NET Core) template, which states
A project template for creating a class library as a NuGet package that can target any platform
Any platform! Brilliant... But I can't get it to work. After a lot of fiddling, I currently have the following project.json (I've probably completely broken it by now):
{
"title": "My Really Useful Class Library",
"copyright": "Copyright © 2015-16 Tigra Astronomy, all rights reserved",
"description": "Really neat stuff",
"language": "en-GB",
"version": "1.0.0-*",
"dependencies": {
"JetBrains.Annotations": "10.1.4",
},
"frameworks": {
"netstandard1.5": {
"imports": "dnxcore50",
"dependencies": {
"NETStandard.Library": "1.5.0-rc2-24027",
"System.Linq.Expressions": "4.0.11-rc2-24027"
}
}
"net46": {
"frameworkAssemblies": {
"System.Collections": "4.0.*"
},
"dependencies": {}
}
},
"buildOptions": {
"xmlDoc": true
}
}
The next thing I did was create my .NET Framework 4.6 project in the same solution, and try to reference the class library. It lets me add the reference but I'm getting build errors, unresolved symbols, R# is unhappy, etc.
I guess I'm not doing it right (no surprise, really, as I'm fumbling in the dark).
I've read some of the docs about TFMs, frameworks and libraries but none of it really makes much sense.
What do I really need to put in my class library's project.json, so that I can reference it from my .net framework 4.6 app, and also from UWP and ASP.NET Core RC2 apps? Is this really the right approach or have I started out on the wrong foot?
Right now there are two ways of creating C# projects: xproj and csproj. Assuming we're using project.json for both of them, that still works differently for the project types -- for xproj, the project.json contains everything needed to build the project; for csproj, it only contains the nuget dependencies.
That said, some project types, like UWP, cannot be built with xproj due to needing a more complicated build pipeline than what xproj/project.json supports. (BTW, this was one key reason for moving back to msbuild.)
There are also two ways of creating a .NET Standard-based class library: you can use xproj with project.json, as you've done, or you can create a regular csproj "Portable Class Library" project. With VS 2015 Update 3 RC, you can change the PCL to target a .NET Standard version (netstandard1.x instead of a PCL profile, 259, etc).
If you use a csproj-based class library to target netstandard1.x, things should just work for you when adding project references. Note that UWP currently supports up to netstandard1.4 based on the platform map. The challenge is if you want to use an xproj/project.json-based project instead. One key reason for using xproj today is to enable cross-compiling between multiple target frameworks. That is to say, create more than one output from your project. That's different than creating a single output that can be referenced from any compatible project. Both have their uses, it depends on your needs.
If you decide to create an xproj-based class library, there's a workaround you can use to reference it from a UWP project or any other compatible project type if the "Add References" dialog doesn't work (which it doesn't as csproj->xproj is pretty much broken). Instead of using the dialog, edit your UWP csproj to point to the output of the xproj like this:
<Reference Include="System.Reactive.Interfaces">
<HintPath>..\System.Reactive.Interfaces\bin\$(Configuration)\netstandard1.0\System.Reactive.Interfaces.dll</HintPath>
</Reference>
The above snippet is taken from the Rx.NET UWP Test Runner here
If you do this, you'll also need to add build dependency from your UWP project to your xproj since MSBuild/Visual Studio won't know about it and build things in the wrong order. To do this, right click on your UWP project in the Solution Explorer, then select "Build Dependencies -> Project Dependencies". In that dialog, check the box for your xproj to ensure that VS/MSbuild knows to build that one first.
You can see the full Rx.NET solution here, which includes xproj->xproj references and the UWP -> xproj references I mention above.
The new project templates/.xproj works a bit differently. The new Class Libraries (and application templates) produce nuget packages, rather than plain assemblies.
Within that nuget package all targets are packed into it. That being said, you add the new project same way as you add any other nuget package: You put the nuget into a nuget feed, reference this in Visual Studio and then fetch it from there.
If you don't have a nuget server running (Visual Studio Team Services + NuGet package, myget, self-hosted) you can also put the packages into a folder (local or network share) and add this folder as a nuget source.
If that's "too" much work, you can also create two projects into one folder: A *.csproj and a *.xproj. The *.csproj targets the .NET 4.6 Framework and the *.xproj stays as you pointed above and has multiple targets. With this setup, you can normally reference the project the way you used before (if they are in the same solution), by simply adding an reference.

ASP.NET 5 System.Runtime.Caching

I am converting an existing .Net 4.5 MVC 5 project to a new ASP.NET 5 project. One of my files is referencing the System.Runtime.Caching namespace but on moving this file to the new project this namespace cannot be found.
I have added System.Runtime as a dependency in the new project, but the .Caching bit seems to be missing from this. Has anybody experienced a similar problem?
You need to bring in 'Microsoft.Extensions.Caching.Memory' using the following line in your project JSON.
"dependencies": {
"Microsoft.Extensions.Caching.Memory": "1.0.0"
}
Current documentation can be found here.
https://docs.asp.net/en/latest/performance/caching/memory.html
To use the System.Runtime.Caching namespace in an ASP.NET application, you must add a reference to the namespace.
To add a reference to the Website
In Solution Explorer, right-click the name of the Web site and then
click Add Reference.
Select the .NET tab, select System.Runtime.Caching, and then click
OK.
Ref: https://msdn.microsoft.com/en-us/library/ff477235(v=vs.110).aspx#Anchor_2

Does the direct usage of an indirectly referenced assembly always work in ASP.NET 5?

I thought in ASP.NET 5 you'd have to rely on nuget packages, or the bin wrapper (which never worked for me btw.) if you want to reference an assembly that does not come from a project that is in the same solution as the web/dnx project.
Now I have an ASP.NET 5 RC1 project where I reference a 4.5.1 standard class library project from the same solution like this:
"frameworks": {
"dnx451": {
"dependencies": {
"MyProject.Utilities": "1.0.0-*"
}
}
}
(I don't use "dnxcore50": { })
Everything gets wrapped as expected, MyProjects.Utilities and its required assemblies. What I think is funny is that now in my web project I cannot only use MyProject.Utilities but also all the assemblies MyProject.Utilities references. It doesn't matter if I referenced the assembly via nuget from MyProject.Utilities or if I added an assembly via Add Reference -> Browse from MyProject.Utilities. I can use both types of indirectly referenced assemblies directly in my web project just so out of the box.
I think this is great, will this stay this way? Was this always meant to work this way?
No, this won't continue to work. In fact it stopped working with ASP.NET core RC 2. Dependencies now have to be specified explicityla as NuGet packages in the web project in order to use them from there.
Visual Studio 15 will probably allow to reference local dlls without the need for packages just like non core projets though.

Reference a csproj from same solution as xproj

I have a solution with the following projects:
MySolution.sln
- MySolution.Client.csproj
- MySolution.Service.csproj
- MySolution.Models.csproj
- MySolution.Server.xproj
MySolution.Models is a simple class library which contains shared code that is referenced by MySolution.Client and MySolution.Service - and I would like to reference it in MySolution.Server.
The GUI in VS 2015 RC1 lets me add the reference by right clicking References -> Add Reference. I then see all my projects under Projects -> Solution.
I select MySolution.Models and click Ok, after which I receive the following error in the output log:
Errors in ...PathToSolution\MySolution.Server\project.json
Unable to locate MySolution.Models >= 1.0.0-*
It really feels like this should work, since the GUI allows me to add the reference without any hiccups.
So the first thing to understand is DNX projects have no understanding of traditional .net projects. They don't read or parse csproj files. This is done to keep them cross platform and cross IDE compatible (csproj is a distinctly windows and VS specific thing).
When you add a reference to a "legacy" (I use legacy to mean a .net 4.x csproj based project) behind the scenes the IDE will run dnu wrap but it looks like in your case something broke.
The following should be done automatically.
In solution root global.json a folder "wrap" should be added to the
projects property.
A folder off the root named "wrap" will be created if it doesn't exist.
A /wrap/project.json will be created/updated with a path to the assembly (dll).
Add a reference to the assembly and version to the referencing project's project.json file.
So first thing to check is make sure you have a "wrap" folder and wrap reference in projects property of solution.json. If you don't then likely something "broke". Try removing the reference rebuilding and adding the reference back. Check the build output window for any errors (VS is still RC so there are something error which probably should be halting that are not).
Look for a project.json in the wrap folder. It should look something like this:
{
"version": "1.0.0-*",
"frameworks": {
"net452": {
"wrappedProject": "../../LegacyClassLibrary/LegacyClassLibrary.csproj",
"bin": {
"assembly": "../../LegacyClassLibrary/obj/{configuration}/LegacyClassLibrary.dll",
"pdb": "../../LegacyClassLibrary/obj/{configuration}/LegacyClassLibrary.pdb"
}
}
}
}
Note the framework version. If there is a mismatch then it will fail resolving the dependencies. For example if your MySolution.Models targets .Net 4.6 and thus when wrapped has a dnx46 framework reference but your MySolution.Server project has a reference to dnx452 (in the project.json for MySolution.Server) then it will fail when resolving the dependency to MySolution.Models.
The you quoted could probably be improved. It means that it could not resolve the dependency due to one of the following reasons
It could not find a MySolution.Models assembly (either source code or compiled dll) based on the paths it uses (starting from projects parameter in global.json).
It found a MySolution.Models assembly (either source code or compiled) BUT it was an invalid version. Check version in Models project vs the reference in Server project.json.
It found a MySolution.Models assembly but it can't resolve framework dependencies (i.e. Models requires dnx46 but Server only targets dnx452).
In my experience the third one if the most common. For the DNX templates in VS 2015 RC the default full framework being targeted is dnx452 (or is it dnx451?). New csproj projects will be 4.6 (dnx46) by default and existing projects could be just about anything.
An alternative solution:
I have found the following alternative to result in easier dependency management. If MySolution.Models will only be used by DNX projects then just convert it to a DNX project move it into the source folder and reference it directly. It will be part of the source compilation and you gain the benefits of dynamic compilation.
If MySolution.Models will be referenced by both DNX and legacy (csproj) projects then you can create a side-by-side xproj and project.json files for Models. They will be ignored by the legacy project. In essence you have both a legacy and DNX project using the same source files. You can then just like above reference it directly. Keep in mind the folder structure if the models folder is not under /src (and it probably isn't if this was an existing project) then you will either need to move it or add a reference to the folder in global.json. That sounded more confusing that it really is. Just keep in mind for a DNX project the global.json defines the relative paths to where DNX can find source code. The DNX also can resolve dependencies by nuget or searching the GAC but that is beyond what you are trying to do.