PackageReference Aliases are ignored on parent projects - .net-core

Actually I'm trying to use the two libraries Microsoft.Graph and Microsoft.Graph.Beta in parallel within my solution.
To get this to work, you can use aliases within references. For this purpose you first have to write within your .csproj file something like this:
<PackageReference Include="Microsoft.Graph" Version="4.25.0" />
<PackageReference Include="Microsoft.Graph.Beta" Version="4.40.0-preview">
<Aliases>GraphBeta</Aliases>
</PackageReference>
After that within your .cs file at the top you can write something like this:
extern alias GraphBeta;
using Beta = GraphBeta.Microsoft.Graph;
using Microsoft.Graph;
And within your code you can access both parts individually like this:
var client = new GraphServiceClient(new HttpClient());
var betaClient = new Beta.GraphServiceClient(new HttpClient());
So far so good.
Unfortunately within my solution I'm using the above code within a library project. This library project is referenced by my application project and within my application project I also need to make some calls to Microsoft.Graph only. After adding the beta package to my library project I'm getting compiler errors from my application project like this:
error CS0433: The type 'IMessageAttachmentsCollectionPage' exists in both 'Microsoft.Graph.Beta, Version=4.40.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' and 'Microsoft.Graph, Version=4.25.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
So it seems, the given alias to the NuGet package within my library project doesn't flow upwards to my application project. My application project doesn't have any direct reference to any of the both Graph libraries, it just consumes it through the indirect dependency from the library project.
Also trying to add the extern alias line to the file within my application project results in this error message:
error CS0430: The extern alias 'GraphBeta' was not specified in a /reference option
Any solution available to either make the alias flowing upwards to my application project or to make the Beta dependency internal to the library project?

After some further try-and-error, my current solution is to add a reference to the Graph.Beta assembly (including the alias) explicitly to the application project. After that the error message is gone.
But this is just a simplified example. In my real project I have a deeper structure of multiple library projects and now every project that needs to use the Microsoft.Graph namespace needs to get an explicit package reference with the alias to the Beta package. For example if you have a test project for your library project, it must explicitly reference the Beta package including the alias if it tries to use any type from the Microsoft.Graph namespace within a test.

Related

Why does a force downgrade causes an assembly load exception in .Net Core?

I have a sample solution with a console and library project. Both reference the same nuget but a different version. The console project also has a reference to the library project. So the structure is like this:
- Solution
- ConsoleApp
- Project Reference: Library
- Nuget: NServiceBus.RabbitMQ (5.2.0)
- Library
- Nuget: NServiceBus.RabbitMQ (6.0.0)
You can find the solution here.
Since Nuget uses the nearest wins rule, the nuget package that gets resolved is version 5.2.0. This is what I want, so far so good. But when I run the application and run a method of the Library I get the following exception:
Could not load file or assembly 'NServiceBus.Transport.RabbitMQ, Version=6.0.0.0, Culture=neutral, PublicKeyToken=9fc386479f8a226c'. The located assembly's manifest definition does not match the assembly reference. (0x80131040)
In .NET Framework I would solve this with an assembly redirect. But that isn't available in .Net Core. I always thought that .Net Core solves this automatically by using the deps.json file. There I see the following statement:
"Library/1.0.0": {
"dependencies": {
"NServiceBus.RabbitMQ": "5.2.0"
},
"runtime": {
"Library.dll": {}
}
}
But still at runtime he tries to resolve the 6.0.0 version. I'm using the latest .Dot Net 3.1.X SDK.
I'm I doing something wrong or does this seem like a bug?
For the record, this is a simple sample project. The actual situation where I need this is much more complex. I also do understand that doing this can cause runtime exceptions while running the application.
It appears to be by design.
A little bit of searching, I found this: https://github.com/dotnet/fsharp/issues/3408#issuecomment-319466999
The coreclr will load an assembly of the version or higher than the reference. If the assembly discovered is lower than the reference then it fails.
Also this: https://github.com/dotnet/sdk/issues/384#issuecomment-260457776
downgrading the assembly version isn't supported on .NET Core
So, to confirm, I spent much more time than I intended looking/searching through https://github.com/dotnet/runtime. Eventually I found the assembly version compatibility method: https://github.com/dotnet/runtime/blob/172059af6623d04fa0468ec286ab2c240409abe3/src/coreclr/binder/assemblybindercommon.cpp#L49-L53
It checks all the components of the version separately, but if we look at just one, we can see what it's doing:
if (!pFoundVersion->HasMajor() || pRequestedVersion->GetMajor() > pFoundVersion->GetMajor())
{
// - A specific requested version component does not match an unspecified value for the same component in
// the found version, regardless of lesser-order version components
// - Or, the requested version is greater than the found version
return false;
}
As the comment says, the loader will reject the assembly if the assembly's version is lower than the requested version. In your case, assuming that the assembly version matches the package version (which it doesn't have to), your library is requesting version 6.0.0, but the assembly loader/binder, found version 5.2.0 on disk, which is lower. Hence, it rejects that dll, keeps looking, but then can't find a suitable version of the assembly on the probing path and eventually throws the FileLoadException.
What's not clear to me is if this assembly compatibility is checked only on the default assembly loader, or even if you add your own event handler to AssemblyLoadContext.Default.Resolving. You could try adding your own handler and when it requests the assembly of the higher version, you return the lower version assembly anyway. It might be a way to work around the issue.

Vaadin Flow 14, Jetty embedded and static files

I'm trying to create app based on Jetty 9.4.20 (embedded) and Vaadin Flow 14.0.12.
It based on very nice project vaadin14-embedded-jetty.
I want to package app with one main-jar and all dependency libs must be in folder 'libs' near main-jar.
I remove maven-assembly-plugin, instead use maven-dependency-plugin and maven-jar-plugin. In maven-dependency-plugin i add section <execution>get-dependencies</execution> where i unpack directories META-INF/resources/,META-INF/services/ from Vaadin Flow libs to the result JAR.
In this case app work fine. But if i comment section <execution>get-dependencies</execution> then result package didn't contain that directories and app didn't work.
It just cannot give some static files from Vaadin Flow libs.
This error occurs only if i launch packaged app with ...
$ java -jar vaadin14-embedded-jetty-1.0-SNAPSHOT.jar
... but from Intellij Idea it launch correctly.
There was an opinion that is Jetty staring with wrong ClassLoader and cannot maintain requests to static files in Jar-libs.
The META-INF/services/ files MUST be maintained from the Jetty libs.
That's important for Jetty to use java.util.ServiceLoader.
If you are merging contents of JAR files into a single JAR file, that's called a "uber jar".
There are many techniques to do this, but if you are using maven-assembly-plugin or maven-dependency-plugin to build this "uber jar" then you will not be merging critical files that have the same name across multiple JAR files.
Consider using maven-shade-plugin and it's associated Resource Transformers to properly merge these files.
http://maven.apache.org/plugins/maven-shade-plugin/
http://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html
The ServicesResourceTransformer is the one that merges META-INF/services/ files, use it.
As for static content, that works fine, but you have to setup your Base Resource properly.
Looking at your source, you do the following ...
final URI webRootUri = ManualJetty.class.getResource("/webapp/").toURI();
final WebAppContext context = new WebAppContext();
context.setBaseResource(Resource.newResource(webRootUri));
That won't work reliably in 100% of cases (as you have noticed when running in the IDE vs command line).
The Class.getResource(String) is only reliable if you lookup a file (not a directory).
Consider that the Jetty Project Embedded Cookbook recipes have techniques for this.
See:
WebAppContextFromClasspath.java
ResourceHandlerFromClasspath.java
DefaultServletFileServer.java
DefaultServletMultipleBases.java
XmlEnhancedServer.java
MultipartMimeUploadExample.java
Example:
// Figure out what path to serve content from
ClassLoader cl = ManualJetty.class.getClassLoader();
// We look for a file, as ClassLoader.getResource() is not
// designed to look for directories (we resolve the directory later)
URL f = cl.getResource("webapp/index.html");
if (f == null)
{
throw new RuntimeException("Unable to find resource directory");
}
// Resolve file to directory
URI webRootUri = f.toURI().resolve("./").normalize();
System.err.println("WebRoot is " + webRootUri);
WebAppContext context = new WebAppContext();
context.setBaseResource(Resource.newResource(webRootUri));

CS0121 errors using DryIoc when migrating from PCL to netstandard Xamarin.Forms

I needed to migrate a Xamarin.Forms project from PCL to .netstandard. I use the PCL compatibility nuget package to consume PCLs referenced in the project but I am having a problem with DryIoc that I'm not sure why it is happening. I figured maybe someone here has seen this and can help. Basically I'm getting CS0121 errors (call is ambiguous between 'method1' and 'method2' ) One of the errors is below. I replaced part of the path with the ~ but it looks like it is confused with itself.
~/.nuget/packages/dryioc/2.12.7/contentFiles/cs/any/Container.cs(56,56): Error CS0121: The call is ambiguous between the following methods or properties: 'DryIoc.ReflectionTools.GetFieldOrNull(System.Type, string)' and 'DryIoc.ReflectionTools.GetFieldOrNull(System.Type, string)' (CS0121) (Masterflex)
Thanks!
Fixed this error by replacing the PackageReference of DryIoc with a Reference node in the csproj file:
<Reference Include="DryIoc">
<HintPath>..\packages\DryIoc.dll.4.1.0\lib\netstandard2.0\DryIoc.dll</HintPath>
</Reference>

InvalidOperationException: Could not find 'UserSecretsIdAttribute' on assembly

After deploying ASP.NET Core app to azure and opening the site, I get the following error:
InvalidOperationException: Could not find 'UserSecretsIdAttribute' on
assembly '******, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null'.
The exception details also include that the error happens at Startup.cs on this line of code:
builder.AddUserSecrets();
Thank you
There was an update to the user secrets module just recently. Version 1.0.1 and up now requires you specify an assembly-level attribute for the id of the user secrets, or as a fallback, the way it was previously in project.json.
Here is the announcement on GitHub: https://github.com/aspnet/Announcements/issues/209
You can define the secrets id in the .csproj like this:
<PropertyGroup>
<UserSecretsId>aspnet-TestApp-ce345b64-19cf-4972-b34f-d16f2e7976ed</UserSecretsId>
</PropertyGroup>
This generates the following assembly-level attribute. Alternatively, instead of adding it in the .csproj file, you can of course add it yourself e.g. to Startup.cs:
[assembly: UserSecretsId("aspnet-TestApp-ce345b64-19cf-4972-b34f-d16f2e7976ed")]
Also, you should use:
builder.AddUserSecrets<Startup>();
It will search for that attribute in the assembly of the given type, in this case I used the Startup class.
Note: this will be deprecated in 2.0: (1.0.2 and 1.1.1 have marked it obsolete)
builder.AddUserSecrets();
I checked the source code for the user secrets configuration, and calling AddUserSecrets() without the type does this:
var attribute = entryAssembly.GetCustomAttribute<UserSecretsIdAttribute>();
if (attribute != null)
{
return AddUserSecrets(configuration, attribute.UserSecretsId);
}
// try fallback to project.json for legacy support
try
{
var fileProvider = configuration.GetFileProvider();
return AddSecretsFile(configuration, PathHelper.GetSecretsPath(fileProvider));
}
catch
{ }
// Show the error about missing UserSecretIdAttribute instead an error about missing
// project.json as PJ is going away.
throw MissingAttributeException(entryAssembly);
It's trying to find the UserSecretsId attribute on your assembly, and failing that, checking if it could find it in project.json. Then (as commented) returns an error about the missing attribute as they wouldn't want to complain about project.json anymore as it is being deprecated.
I want to add to this answer, for those in my situation.
I am writing a .NET Core console app, trying to use the secrets manager (not sure it's meant for console apps). The only way I was able to rid myself of the error was using the assembly level attribute on the assembly where I was using the secrets manager.
As I said, I am not sure if the secrets manager is meant for console apps. So maybe there is an issue with .xproj files vs. .csproj files.
My .NET Core 3.1 Worker Service required additional setup (more than a Web project).
In Program.cs in the CreateHostBuilder method I needed this:
.ConfigureAppConfiguration((ctx, builder) =>
{
// enable secrets in development
if (ctx.HostingEnvironment.IsDevelopment())
{
builder.AddUserSecrets<Worker>();
}
})
But (unlike my Web project) I explicitly needed to add this nuget package:
install-package Microsoft.Extensions.Configuration.UserSecrets
After that I could access secrets.

Prism + Log4Net build error : "Cannot resolve dependency to assembly log4net"

I am trying to add log4net support in a Prism application. Unfortunately i get the following error for each prism modules :
Error 101 Unknown build error, 'Cannot resolve dependency to assembly 'log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821' because it has not been preloaded. When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event.' [PRISM MODULE 1 PROJECT NAME]
Error 101 Unknown build error, 'Cannot resolve dependency to assembly 'log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821' because it has not been preloaded. When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event.' [PRISM MODULE 2 PROJECT NAME]
ect ...
I set up the logger as follow :
I added log4net assembly reference to both the main app, and the bootstrapper.
I added log4net configuration to the app.config file.
I added [assembly: log4net.Config.XmlConfigurator(Watch = true)] to the App.xaml.cs file.
I created a logger implemeting ILoggerFacade and overwrote CreateLogger() in the Bootstrapper to make it return my custom logger.
That's it.
If i try to build, it fails with the error mentioned above for each prism module project.
The only work around i found was to manually add log4net references to each prism modules projects which, i think, kind of defeats the abstraction between ILoggerFacade and the dependencies of the actual implementation.
The error suggest to use the ReflectionOnlyAssemblyResolve event, but i don't see how it could help here as it is a build error, not runtime.
Any suggestion ? :-)
Ok i found the problem, each modules were referencing the bootstrapper which is just wrong, i removed that dependency and VOILA, fixed !

Resources