I have a directory labeled unit-testing-using-nunit as defined in the Microsoft documentation. My issue is being unable to reference the classes I want to test. I'm still relatively new to .NET so I'm having issues understanding how to properly implement these unit tests.
After implementing Usings.cs
I end up with this error:
I feel like once I get a hint for how to reference /API/Entities/AppUser.cs I'll be able to take it from there.
You're going to want to add a project reference in Entities.Tests.csproj to the API project like so:
<ItemGroup>
<ProjectReference Include="..\..\API\API.csproj" />
</ItemGroup>
Related
I'm trying to experiment with a specific source generator library and I wanted to try and modify some parts of it. To do that cloned the repository for that library and referenced two .csproj files in it from my own .NET 7 project. This looks at first like it would work, VS Code recognized the imports and doesn't complain, but the actual source generator part does not seem to work.
The specific library I'm using is Mapperly and I referenced it in my .csproj file as following:
<ItemGroup>
<ProjectReference Include="..\mapperly\src\Riok.Mapperly.Abstractions\Riok.Mapperly.Abstractions.csproj" />
<ProjectReference Include="..\mapperly\src\Riok.Mapperly\Riok.Mapperly.csproj" />
</ItemGroup>
The error I get is
Partial method 'MyMapper.MapToReadModel(MyEntity)' must have an implementation part because it has accessibility modifiers.
This code compiles if I add the complete nuget package of this library, and it stops working with this error if I remove the nuget package and reference the projects as I've shown. My understanding of this error would be that it indicated that the source generator either did not generate any source, or the build process doesn't use the generated source for some reason. The way this library works is that you define partial methods for mapping, and the source generator fills in the actual code, so that last step seems to fail in this case.
Obviously the way I'm referencing this library makes it behave differently than what happens when I add the full nuget package. The repository has more .csproj file, but those are all tests or samples. It also has a .sln file, but it seems I can't reference that directly.
I'm also using VS Code while the information I could find on this often assume Visual Studio, not sure if there are any limitations here with VS Code only.
What is the proper way to reference a local library that contains a source generator so that it will work properly in VS Code? Or am I misunderstanding the problem here and the cause is something else entirely?
The library you are referencing is not just regular library but a source generator, you need to reference them in a special way. You can see an example in documentation for source generators. Also note how authors of this library reference their source generator project in their tests:
<!-- For local development, simply reference the projects. -->
<ItemGroup Condition="'$(MapperlyNugetPackageVersion)' == ''">
<ProjectReference Include="..\..\src\Riok.Mapperly\Riok.Mapperly.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\src\Riok.Mapperly.Abstractions\Riok.Mapperly.Abstractions.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
</ItemGroup>
Where is CircuitHandler in Blazor .NET 6?
As I am upgrading packages to .NET 6, I noticed in nuget package manager that Microsoft.AspNetCore.Components.Server package was marked obsolete and so assuming CircuitHandler was moved to another package and that one is no longer needed (As they have done in the past with IAsyncEnumerable and Span<T>), I removed the package. Then I realized that package has completely been removed from nuget browser altogether, and there is no word anywhere I can find on any alternative for .NET 6. MSDN even has Blazor .NET 6 articles talking as if CircuitHandler is easily found in Microsoft.AspNetCore.Components.Server namespace, but no mention of what package or how to get access to that namespace! Example here
I figured this out, as I noticed the type exists in my BlazorApp project, but not my library project. It was a matter of figuring out what the differences are, and it turns out that you need to put <Project Sdk="Microsoft.NET.Sdk.Web"> in the project file instead of <Project Sdk="Microsoft.NET.Sdk">. So just adding the .Web part fixed the issue!
Note: Now my library won't build because it says I need a static Main method entry point. So it seems they aren't allowing CircuitHandlers in class libraries anymore, they must all reside in the Web App itself. Total bummer!
The real solution is to add the following to your project file. What a runaround this was for me to figure out! And I could not find this documented anywhere!
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
TL:DR;
Is there a way to resolve a "partial" circular dependency between C# projects using MSBuild wizardry? 🧙‍♂️
Long version
We have a project A that depends on project B. A.csproj looks like this:
<ItemGroup>
<ProjectReference Include="..\projectB\B.csproj" />
</ItemGroup>
Now project B needs a type from Project A. VS prevents us from adding a reference to project A (circular dependency) but it doesn't complain when we add a link to the specific file, like so in B.csproj:
<ItemGroup>
<Compile Include="..\projectA\ClassA.cs" Link="ClassA.cs" />
</ItemGroup>
The problem appears when we use ClassA on project A. Now VS complains (rightfully) about multiple declarations of ClassA (CS0436 - we have set warnings as errors), one from each project.
I am aware that this shows that our design is flawed and as other answers here in SO indicate the right solution would be to refactor. If this is not possible due to time constraints, is there any MSBuild trick that would allow projectA to compile?
For reference, things that I tried and don’t work
In A.csproj: set ReferenceOutputAssembly=false from project B.
=> Would work if project A doesn’t need anything from B.
In B.csproj: include and then remove 🥴
<ItemGroup>
<Compile Include="..\circdependency\ClassA.cs" Link="ClassA.cs" />
<Compile Remove="ClassA.cs" />
</ItemGroup>
In B.csproj: CopyToOutputDirectory=Never
Other options that work but are not ideal:
Suppressing the warning.
Duplicating ClassA in project B and using a different namespace, making it internal etc.
I have a library project that extends some functionality on EntityFrameworkCore. I'm looking to support both 2.* and 3.*. My project is setup like so:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp3.0</TargetFrameworks>
[...]
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.0' ">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
</ItemGroup>
[...]
</PropertyGroup>
</Project>
In the code I'm using the function EntityTypeExtensions.FindProperty(...). The signature of this function changes between 2.2.6 and 3.0.0.
The project's code (incorrectly?) uses the signature for 2.2.6. This compiles properly (which shouldn't be the case?) in both target frameworks.
I have a unit test project that multi-targets and has conditional references, much like the original project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;netcoreapp2.0</TargetFrameworks>
[...]
</PropertyGroup>
[...]
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="2.2.6" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.0' ">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.0.0" />
</ItemGroup>
[...]
</Project>
All unit tests (incorrectly?) pass in both target frameworks.
Note that even though it builds and tests pass, when the library is used in a netcore3 project (which references efcore 3.0.0 directly) it throws the following exception. Which seems completely reasonable, I just don't understand why it allowed me to get to this point.
System.MissingMethodException: Method not found: 'Microsoft.EntityFrameworkCore.Metadata.IProperty Microsoft.EntityFrameworkCore.EntityTypeExtensions.FindProperty(Microsoft.EntityFrameworkCore.Metadata.IEntityType, System.Reflection.PropertyInfo)'.
Questions:
Is there a way around this so it gets picked up as an error/warning/something, at least, during the build?
Is the solution to this to use preprocessor directives around the call to .FindProperty(...) and based on the framework make the correct method call? Isn't there a way to do this based on the version of efcore instead of the dependency?
Is there a way to unit test this properly with the different packages? Right now as it is, I expected the unit tests to fail in one of the versions since the method does not exist.
Source repository and specifically the call to FindProperty can be found here.
Sample netcore3 project that results in a MissingMethodException when calling the library can be found here.
Stack trace of the exception can be found here.
I have good news and bad news. The good news is that the problem is with your package, and everything works just how you appear to believe it should work. The bad news is I don't know how your package got incorrectly authored.
Steps to verify:
Download Panner.Order version 1.1.0 from nuget.org (you've published 1.1.1 since asking this questions, which has the same, but different, problem). If you have NuGet Package Explorer installed, open the nupkg with that, expand the lib/ folder and double click each of the .dll files. Alternatively you can extract the nupkg as a zip file then use ILSpy or ILDasm or whatever else you want to inspect the assemblies. Notice that both the netstanard2.0 and netcoreapp3.0 assemblies have the same assembly references. In particular the Microsoft.EntityFrameworkCore.dll reference is for version 2.2.6.0, even though we'd expect the netcoreapp3.0 version to use version 3.0.0.0. Therefore I conclude that your netstandard2.0 assembly was copied incorrectly into the netcoreapp3.0 folder of your package. Your 1.1.1 package has the opposite problem. Both the netstandard2.0 and netcoreapp3.0 folders contain the netcoreapp3.0 assembly, so your package doesn't work with projects that try to use the netstandard2.0 assembly.
However, I have no idea why this happens. When I clone your repo and run dotnet pack and check the generated nupkg, I can see that the netstandard2.0 and netcoreapp3.0 assemblies have different references, so I'm confident that the package I generated locally should work. You need to investigate why the packages you publish are not being generated correctly.
To quickly answer your questions:
Is there a way around this so it gets picked up as an error/warning/something, at least, during the build?
It will, as the problem was not with the project, but with the package. If you multi-target your project and call an API that does not exist in at least one of the TFMs, you will get a compile error.
Is the solution to this to use preprocessor directives around the call to .FindProperty(...) and based on the framework make the correct method call? Isn't there a way to do this based on the version of efcore instead of the dependency?
When you call APIs that are different in different TFMs, yes, you can use #if to change your code per project TFM, as described in ASP.NET Core's docs when migrating to 3.0.
I'm going to ignore the "based on the version of efcore" because I'm a detail oriented person, and I don't want to write one thousand words for something that ultimately doesn't matter. The key is that in this scenario, you don't need to. You used conditions on your package references to bring in a different version of efcore per project TFM, so each time your project gets compiled, it's using a different version of efcore, but only one version per compile target. Therefore you don't need runtime selection of different versions of efcore.
Is there a way to unit test this properly with the different packages? Right now as it is, I expected the unit tests to fail in one of the versions since the method does not exist.
You multi-target your test project, but I see you've done that already. Since you're using a project reference, the test won't detect package authoring problems like what's happening.
If you really want to test the package, rather than your code, you could use a nuget.config file to add a local folder as a package source, then your multi-targeting test project references the package, not the project. You'd probably want to also use the nuget.config file to set the globalPackagesFolder to something that's in .gitignore because NuGet considers packages to be immutable and if a debug version of your package gets into your user profile global packages folder, every project you use on that machine (that uses your user profile global packages folder) will use that debug version, making it more difficult for you to make updates. For customers who want to test packages, rather than projects, I highly recommend using SemVer2's pre-release labels and create a unique package version for every single build to reduce the risk of testing a different version than you intend.
Using package reference rather than project reference is a pain, because it's no longer as simple as writing code and then running the test. You'll need to change code, compile the project that gets generated into a package, copy the package into the package source folder if you haven't automated that, update the package version in your test project, then compile and run the test project. I think you're better off keeping the project reference. Fix the package authoring problem and then trust the tooling works.
Not to directly answer all questions above one by one, just to describe the cause of the original issue and some suggestions.
In the code I'm using the function
EntityTypeExtensions.FindProperty(...). The signature of this function
changes between 2.2.6 and 3.0.0.
According to your description, I assume you may use code like EntityTypeExtensions.FindProperty(entityType, propertyInfo); in your original project.
For Microsoft.EntityFrameworkCore 2.2:
FindProperty (this Microsoft.EntityFrameworkCore.Metadata.IEntityType entityType, System.Reflection.PropertyInfo propertyInfo); second parameter=>PropertyInfo
For Microsoft.EntityFrameworkCore 3.0:
FindProperty (this Microsoft.EntityFrameworkCore.Metadata.IEntityType entityType, System.Reflection.MemberInfo memberInfo); second parameter=>MemberInfo
However, please check PropertyInfo Class, you'll find:
Inheritance: Object->MemberInfo->PropertyInfo
And I think that's the reason why the project's code uses the signature for 2.2.6 but it compiles properly in both target frameworks. And it's the cause of other strange behaviors you met after that...
So for this issue, you could use the signature for 3.0.0(MemberInfo) in code instead of 2.2.6(PropertyInfo) to do the test. I think the build will fall as you expected. And as Heretic suggests in comment, for multi-target project, use #if is a good choice.
Hope all above makes some help and if I misunderstand anything, please feel free to correct me :)
Summary
For my CS capstone project we are encountering an issue with a class name existing in two different dependencies. Specifically, we are using a dependency for using MySQL with Entity Frame and one for just connected and executing MySQL queries directly.
Background
The non-EF is owned by a component outside of the project, and this database is one of our main ways of interacting with the database. It has been requested by the client/mentor that any additions or changes we need be made to a separate database, which is the database EF is connecting to.
The question
My question is essentially about how do I fix error the type <class-name> exists in both..., but I'm more wondering about the root problem of namespace pollution in .Net Core and the courses of action we can take. I have looked into the error and the initial results described a fix that is only applicable in .Net not .Net Core and an explanation that .Net Core does not support aliasing.
Potential fixes
Separate projects - I have asked someone I know who has more experience with .Net, and he suggest making separate projects. While that would obviously work in terms of getting rid of a build error, I do not know how we could make use of one in the main ASP.Net app. I am assuming either both need to be apps or making one into a library. I am also assuming that if it is a separate library it will have the same problem we are having now.
Removing one dependency - I am currently considering that the less than ideal solution is rewriting the code that relies on EF to use the direct MySQL connection dependency. There is less code relying on that EF database, so it would be simpler to rewrite that and some SQL.
Aliasing or full reference - The results I have found that seem to only be applicable to .Net describe using an alias or referencing the full path of the decency in the type. From what I have read, this is not currently supported in .Net Core. If it is, how may I go about it?
using System;
using System.Collections.Generic;
using MySql.Data.MySqlClient;
namespace OVD.API.GuacamoleDatabaseConnectors
{
public class GuacamoleDatabaseConnector : IDisposable
{
private MySqlConnection connection;
...
The error is on the MySqlConnection type and is, in full: GuacamoleDatabaseConnectors/GuacamoleDatabaseConnector.cs(81,16): error CS0433: The type 'MySqlConnection' exists in both 'MySql.Data, Version=8.0.15.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d' and 'MySqlConnector, Version=0.49.2.0, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92' [/Users/markbeussink/Action/OVD/OVD.API/OVD.API.csproj]
Here is the .cs.proj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App"/>
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All"/>
<PackageReference Include="Ldap.NETStandard" Version="1.0.3"/>
<PackageReference Include="MySql.Data" Version="8.0.15"/>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0"/>
</ItemGroup>
</Project>
Just create a new project "class library" and inside this project, you can create an interface which gives you access to a method from one of your component (you need to implement your "component" and its method inside this project). Something like in facade pattern. Then in the rest of your solution, you will use a newly created project reference only. This solution allows you define your own namespace name
It's really bad form for two separate projects to have a type with the same namespace and the same name, for the reason you've just discovered. It is not at all normal or expected that you would run into such a conflict, and you may well never encounter it again in your career.
It looks like this project:
https://www.nuget.org/packages/MySqlConnector/
decided to clobber the namespace of the more official ADO.NET provider for MySQL:
https://www.nuget.org/packages/MySql.Data
by defining a type called: MySql.Data.MySqlClient.MySqlConnection, instead of using MySqlConnector.MySqlClient.MySqlConnection, or somesuch.
The best way forward is to exclude one of these from your projects, and use just the other. Here the obvious choice would be to switch from
https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql/
to
https://www.nuget.org/packages/MySql.Data.EntityFrameworkCore/
But I don't have any opinion on the relative merits of these libraries.
If you can't do this, C# provides a compiler directive for you to alias one of the assemblies with a different namespace. See https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0433
This will in effect add a new outermost namespace level to the offending assembly, so the other MySqlConnection would be known (only in your code) as SomeAlias.MySql.Data.MySqlClient.MySqlConnection.