Reference a csproj from same solution as xproj - asp.net-core

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.

Related

msbuild refuses to copy unsigned dll if it is of lower version than demanded by one of the project dependencies - is it by design?

I have the following situation:
All the involved dlls are unsigned
All the projects in the solution depend on version 1.0.21221.1 of Shared.dll
Some NuGet dependencies of some projects in the solution depend on version 1.0.21237.1 of the same dll.
When the web application is built (let us name it Api) it is expected to copy Shared.dll from $(OutDir) to the $(OutDir)_PublishedWebsites\Api\bin folder. The Shared.dll found in $(OutDir) has the version 1.0.21221.1.
The Shared.dll is NOT copied and the web application fails to run.
Here is the evidence from the binary log:
Exhibit A - The conflict of versions:
Exhibit B - ResolveAssemblyReference instructs NOT to copy Shared.dll:
I understand that msbuild does not like the idea of conflicting versions, but NOT copying the dll produces a downright bug, because the application fails to start.
I understand one can resolve it by adding an assembly binding redirect. But I thought it was unnecessary for unsigned assemblies. Am I understanding wrong or am I missing something?
EDIT 1
Here are my answers to the questions posted in the comments:
(Unfortunately I was asked to obfuscate some keywords, I do not know why)
How exactly does the Api project reference Shared.dll?
As we can see in the exhibit B Shared.dll is a transitive dependency of Api. Indeed, Api depends on Xyz.BusinessApi like this:
<Reference Include="Xyz.BusinessAPI" />
Now that DLL depends on Shared.dll through the respective NuGet dependency, here is a snippet from the project.assets.json file of Xyz.BusinessAPI:
What other projects reference Shared.dll and how?
There are a lot of projects referencing it as a NuGet package at version 1.0.21221.1. The problem is that some projects also reference two other NuGet packages which in turn depend on the version 1.0.21237.1 of the same NuGet package. This is indicated in the RAR output - see the exhibit A.
I would like to emphasize - no project references Shared.dll as a raw dll, only either as NuGet package or indirectly through other NuGets or projects or project dlls. Project dll is a dll of a project from a previously built solution - we do not allow project references to other solutions, so if a project is built in a previous solution, then it would be referenced as DLL in subsequent solutions.
What is the mechanism used to copy from OutDir to _PublishedWebsites\api\bin?
This is the standard web application publishing target _CopyFilesMarkedCopyLocal from C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VisualStudio\v16.0\WebApplications\Microsoft.WebApplication.targets:
<!-- copy any referenced assemblies to _PublishedWebsites\app\bin folder -->
<Copy SourceFiles="#(ReferenceCopyLocalPaths)"
DestinationFiles="#(ReferenceCopyLocalPaths->'$(WebProjectOutputDir)\bin\%(DestinationSubDirectory)%(Filename)%(Extension)')"
SkipUnchangedFiles="true"
Retries="$(CopyRetryCount)"
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/>
How does Shared.dll end up in OutDir?
All of our code is built into the shared bin directory - we set OutDir to the same value for all the projects. Thus all the project binaries and their dependencies, including Shared.dll first end up there.
Are there any double-writes in the binlog?
Yes, but I do not think they are relevant:
The short answer is that your projects indirectly depend on two versions of Shared.dll: 1.0.21221.1 and 1.0.21237.1.
RAR (the MSBuild ResolveAssemblyReference task) inspected all the references of all .dlls and found these two versions. It reported a conflict:
Pay attention how it reported the found file path in square brackets for Shared.dll 21221 and reported [] (meaning no file of such version was found) for 21237.
It's useful to search for these using There was a conflict under($rar) or $warning under($rar).
Now, the OutDir only contains the Shared.dll 21221, so the Shared.dll with version 21237 couldn't be found anywhere.
The trick is to search for Shared.dll under($rar project(api.csproj)) You will find the relevant messages from RAR:
Considered "C:\Xyz\61\bin.link\Shared.dll",
but its name "Shared, Version=1.0.21221.1, Culture=neutral, PublicKeyToken=null"
didn't match the expected name "Shared, Version=1.0.21237.1, Culture=neutral, PublicKeyToken=null".
So, it saw a conflict, decided to unify on a later version (21237), but didn't find the file of that version. So the reference of 21221 was not CopyLocal because it didn't unify on that version, and the reference to 21237 was not resolved because a file of that version was not found.
To resolve this, I recommend adding an explicit reference to Shared.dll of the version 21237 (either via NuGet or via GeneratePathProperty metadata on the package reference: https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#generatepathproperty). If you use GeneratePathProperty you can then reference the .dll directly using $(PkgFoo_Bar)\lib\net472\Shared.dll or similar. Also add a binding redirect from 21221 to 21237 to resolve the conflict. Once the correct version (21237) will be in your OutDir, it will get copied to output correctly.
Hope this clarifies.

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.

Indirect references in VB.NET solution breaking build

Project A references Projects B. Project B references Project C. Project A does not reference Project C.
This builds fine locally. However, on the build server it errors out because Project A does not reference Project C.
Error:
error BC30009: Reference required to assembly 'ProjectC, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' containing the implemented interface 'ProjectC.IFoo'. Add one to your project.
How can I catch this before committing?
Edit:
Here's more info on my issue: http://sstjean.blogspot.com/2006/11/msbuild-cant-find-secondary-references.html
Here are two things you can do as a best practice to ensure your projects are referenced correctly and MSBuild will be able to find your referenced projects correctly.
Use project references instead of referencing dll's. Create a folder called dependencies or libs and place any referenced dll's within this folder.
Check the build order and project dependencies tab for each project by right clicking project and selecting project build order. Ensure that every reference in your project is being built by that project.
MSBuild does not know what a .sln file is. MSBuild reads and parses the .sln file to determine the build order of projects. By having project references MSBuild will be able to traverse and build the projects in the correct order. See below link for more information.
This link also helps explain why you would see different behavior and how to catch it.
Visual Studio Integration (MSBuild)
Within Visual Studio, the solution file and project build ordering are controlled by Visual Studio itself. When building a solution with msbuild.exe on the command line, MSBuild parses the solution file and orders the project builds. In both cases the projects are built individually in dependency order, and project to project references are not traversed. In contrast, when individual projects are built with msbuild.exe, project to project references are traversed.
When building inside Visual Studio, the property $(BuildingInsideVisualStudio) is set to true. This can be used in your project or .targets files to cause the build to behave differently.
Go to your project references and right click Oracle.DataAccess then go to properties and in properties page make sure that specific version is false and copy to local is set to true.

When to use <ProjectReference> in project files?

Summary:
Projects build in wrong order with visual studio and managed C++ and C# projects
Description:
I have a massive (100+ projects) solution file that is building a few projects in the wrong order. The solution file contains the following types of projects:
native C/C++
Managed C++
Managed C#
The solution contains all the proper dependencies between the different types of projects. Ok, so when I build from the command line (Using MSBuild), there is a problem. The dependencies for the managed projects (both C++ and C#) get built in the wrong order. For instance a project will fail to build because a managed dependency is missing. For instance a managed C++ file will have a using declaration that will fail:
#using <foo.dll>
since foo.dll doesn't exist yet.
Which means that foo.dll should have been built before, but wasn't.
Like I mentioned earlier, the dependencies are properly set up in the solution file. For instance, if foo depends on baz, I have this in the solution file...
Project("{C4ABA494-43D0-400A-9917-20E167A12CFD}") = "Foo", "...\Foo.vcxproj", "{5A42640E-9E0A-442B-8A40-AA91AD5444AC}"
ProjectSection(ProjectDependencies) = postProject
...
{2CE32AE0-B129-40BA-B06E-A628FA149AB3} = {2CE32AE0-B129-40BA-B06E-A628FA149AB3}
EndProjectSection
EndProject
...
Project("{C4ABA494-43D0-400A-9917-20E167A12CFD}") = "baz", "...\baz.csproj", "{2CE32AE0-B129-40BA-B06E-A628FA149AB3}"
ProjectSection(ProjectDependencies) = postProject
...
EndProjectSection
EndProject
So the solution file correctly has the dependency. But the dependency in the Foo.vcxproj project is only expressed by the #using directive.
I've read on the visual studio blog that there is a known bug in ordering projects in msbuild.
http://blogs.msdn.com/b/visualstudio/archive/2010/12/21/incorrect-solution-build-ordering-when-using-msbuild-exe.aspx
Their work around is to add an item called to my projects, like this:
<ProjectReference Include="... foo.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
Anyways, my question is: do I need to do this ONLY for my managed C++ projects? Or do I do this for Managed C++ AND C# projects?
(I kind of believe I don't need to do this for C# projects since their dependencies are explicit)
Note: I have tried putting this on ALL projects in my build, and it didn't work so hot, as I got lots of strange build errors in my native projects...
Thanks for any response to this.
I had the same issue, but with C# projects only.
It seems like MsBuild is NOT using solution file dependencies. It is using project references inside the project files to create build order. Try to update all your ProjectReferences to get correct build order. In your case you have to add managed project reference (dependency) into your C++ project file.
The answer to your question is: Yes, you have to do it for both Managed C++ AND C# projects. Setting dependencies inside sln file is not enough if you are building with MSBuild.

Best practice for storing and referencing DLL libraries?

Often times a developer on my team will create a new Visual Studio project and reference a DLL somewhere on their local machine (e.g., C:\mydlls\homersimpson\test.dll). Then, when I get the project from the source control repository, I cannot build the project because I do not have the referenced dll in the exact same location on my machine.
What is the best practice for storing and referencing shared libraries?
I typically create a lib folder in my project, where I put the referenced dll's. Then I point the reference to the dll in the lib folder. This way, every developer can build the project after retrieving from source control.
If it's a project that was built in house, you could also add that project to your solution.
If the assembly is not in the GAC, create a directory called dependencies and add all assemblies there. The folder and the assemblies are added to source control. The rule is that given any project in source control, all that is required to build is to do a checkout and build the project (or run some tool that is also checked into the project).
If you add a folder to the solution and add the assemblies to the solution folder, this also provides a visual cue to the devs that indicates what external dependencies are present... all dependencies are in that directory. Relative paths ensure that Visual Studio can locate the references without a problem.
For large solutions, with 20+ projects, this makes life much easier!
Best practice I would expect would have Your SC repository include and enforce the relative locations of referenced objects for you (usually via a shared path), so you aren't dealing with this issue directly. The original developer should check in this information.
If you check in the actual DLLs into source control, then you can reference them by relative path and all developers will automatically get any dependencies when they next update the project.
Adding a DLL reference by full path would be a developer error just as adding a source file by full path would be an error.
Rule of thumb: If the project isn't a part of the solution, reference released dlls from a source controlled /binshare or /lib directory that is under your solution's source control tree. All external dependencies should have versioned DLLs that go in this /binshare directory.
I understand what your co-worker is doing in regards to convenience. However, that developer's approach is diametrically opposed to proper configuration/build management.
Example: If you use the MS Data Application Block as a dependency in your application, you should reference a properly released binary, instead of getting latest from MS's dev source trunk.
I think this approach is quite the opposite of what I would consider best practice. I think it would be a much better approach to keep the third party binaries out of the source repository and reference them through something like a Maven repository in your build process. Putting the dlls in the source repository unnecessarily bloats the contents of the repository and results in gets of projects taking considerably longer then necessary. It also makes the independent management of the third party binaries' versions obfuscated by not referencing the version by name but rather implied by referencing the dll of a particular version stored in the projects lib folder.
Why not set up a private NuGet-feed? This way, there is only a single copy of a dependency (the NuGet repository) and multiple projects can reference it. Multiple versions of the dependency can coexist, and each project can reference a different version, if necessary. Also, TFS Build can restore the packages at build time.
Configuring VS: https://www.visualstudio.com/en-us/docs/package/nuget/consume