Avoiding or fixing namespace pollution in .Net Core - c#

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.

Related

Running and referencing a source generator library locally from a cloned repository

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>

Having issues referencing a class with NUnit testing

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>

CircuitHandler Equivalent for .NET 6?

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>

Is there a method or a way in Roslyn to enumerate all assemblies used at compile time by a given C# project

I'm developing an application that detects and eliminates useless external references in a given C# project for example a package referenced but not used. For this I need to enumerate all assemblies used either at compile and runtime. For those used at a runtime, I can get names using reflection specifically calling Assembly.GetReferencedAssemblies(). But for those used only at compile-time I think that there is no way to list them without using Roslyn. Have you please any answers to my problematic?
This is just a simple approach, far from perfect. I am sure you can go deeper and play around with Roslyn as much as you want. I am also sure there are much better experts on compilers and Roslyn out there than me :)
Note that this approach does not handle dynamically loaded assemblies. It avoids running the project.
Referenced assemblies
You can use an XML parser instead of Roslyn to analyze the project or solution file. There you will find the list of all referenced packages and project, e.g.:
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MessagePack" Version="2.1.115" />
</ItemGroup>
The above example comes from a .NET Core 3.1 project file.
Update
In order to collect transitive dependencies as well there are several options.
For .NET Core projects you may call dotnet restore and then read the following file $ProjectFolder/obj/project.assets.json. It is a json file that contains all references that were resolved containing both NuGet and runtime packages.
If you want to avoid restoring the project/solution you can start with the packages found in .csproj as described above and use the NuGet Server API to get an array of dependencies for each. Doing this recursively will yield the same result as dotnet restore. NuGet Core wraps the API as C# classes so you can call it directly from your code. Dependencies can be easily queried like here: How to get the dependencies of a NuGet package from private NuGet feed?
Required assemblies
Now you need to identify those that are really necessary. The compilation process takes care of that. It does not (to my humble knowledge) insert any unnecessary references into the compiled code.
Roslyn semantic model
Addressed in this question: Roslyn getting dependencies for class
IL option
You can use Roslyn to compile the given project, then load the generated IL and look for references like [System.Private.CoreLib]. Just throw all referenced assemblies into a HashSet and remove them once you find a matching call in the IL. The resulting set will contain the references that can be removed.
Maybe this project can help viewing the IL. Also SharpLab can be very handy when you need Roslyn output examples.
Load option
If you are fine with loading the compiled project, you can use Assembly.GetReferencedAssemblies() as you indicated in the question instead of analyzing the IL.

Why is Azure Function v2 unable to bind to CloudTable?

I'm trying to run an HTTP triggered v2 function in Visual Studio 2019.
It's supposed to write its output into an Azure Storage Table called "history".
I've decorated one my functions with
[return: Table("history")]
and I make it return a subclass of TableEntity.
This results in an exception about it being "unable to bind Table to CloudTable". The reason for the exception is a check within the CloudStorageAccount client's code:
bool bindsToEntireTable = tableAttribute.RowKey == null;
if (bindsToEntireTable)
{
// This should have been caught by the other rule-based binders.
// We never expect this to get thrown.
throw new InvalidOperationException("Can't bind Table to type '" + parameter.ParameterType + "'.");
}
Another function binds to a CloudTable as an input parameter and suffers from the same exception.
Although binding to CloudTable should work (https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-table#input---c-example---cloudtable) it apparently does not.
Is this a bug in the client SDKs for Azure Storage or am I doing something wrong? I'm referencing these Nuget packages:
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.0.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.DurableTask" Version="1.8.3" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.6" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.29" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
The problem is a version mismatch of two Nuget packages. When creating a new solution I was unable to replicate the issue and binding to CloudTable worked just fine. Comparing to my solution revealed that my function project referenced another project which had a dependency on
WindowsAzure.Storage (9.3.3)
because I needed the TableEntity type in there.
And now it's getting tricky. The functions project has a reference to
Microsoft.Azure.WebJobs.Extensions.Storage (3.0.6)
and that one has a dependency on
WindowsAzure.Storage (9.3.1)
The version difference of 9.3.3 and 9.3.1 leads to the binding problems.
The solution is to either downgrade to 9.3.1 in the referenced project
or
alternatively (and probably recommended): remove WindowsAzure.Storage from the referenced project and replace it with Microsoft.Azure.Cosmos.Table which also contains TableEntity. Important do NOT confuse this with Microsoft.Azure.CosmosDB.Table (notice the "DB") which is being deprecated. Unfortunately, the comments for WindowsAzure.Storage (9.3.3) tell us to change to exactly that incorrect package.
Concusion: it's a hot mess :-)
I had similar problems, I got this when I tried to start my Azure Function V3:
Error indexing method 'Function' Cannot bind parameter 'Table' to type CloudTable. Make sure the parameter Type is supported by the binding. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
As stated, I saw in my project that the WindowsAzure.Storage had a warning sign and stated that it is deprecated.
The fix for me to get my project listening on Table Storage events was to change the using. Apparently I already got a reference to Cosmos in my project using the Microsoft.Azure.WebJobs.Extensions.Storage
Fixing the issue for me by removing the using: using Microsoft.WindowsAzure.Storage.Table; and just replace it with using Microsoft.Azure.Cosmos.Table;
But be aware if you are using the ObjectFlattenerRecomposer.Core it is still using Microsoft.WindowsAzure.Storage.Table and will not work with Microsoft.Azure.Cosmos.Table yet. I have contacted the developer regarding this.

Categories

Resources