Autofac is not loading module from Multi Target .Net Standard library - c#

I'm developing a windows service and I'm referencing a .NET Standard library where I have an Autofac Module, I'm going call this library as A. I have the following PropertyGroup in the csproj:
<PropertyGroup>
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
</PropertyGroup>
And this is the Autofac module quoted earlier:
public class DefaultModule:Module
{
protected override void Load(ContainerBuilder builder)
{
#if net461
builder.Register(context => {
return new BusSettings
{
HostAddress = System.Configuration.ConfigurationManager.AppSettings["HostAddress"],
Username = System.Configuration.ConfigurationManager.AppSettings["Username"],
Password = System.Configuration.ConfigurationManager.AppSettings["Password"],
QueueName=System.Configuration.ConfigurationManager.AppSettings["QueueName"]
};
}).AsSelf();
#else
builder.Register(context => {
var configuration = context.Resolve<IConfiguration>();
return new BusSettings
{
HostAddress = configuration["BusSettings:HostAddress"],
Username = configuration["BusSettings:Username"],
Password = configuration["BusSettings:Password"],
QueueName = configuration["BusSettings:QueueName"]
};
}).AsSelf();
#endif
Now I created .NET Framework console app using 4.61 as Target Framework.And this is the code I use to load the modules:
//Library A is loaded By ExtractCustomAssemblyModules
List<Assembly> assemblies = ExtractCustomAssemblyModules();
containerBuilder.RegisterAssemblyModules(assemblies.ToArray());//Register custom modules
When I execute containerBuilder.Build() I'm not seeing Autofac loading the module and registering the services I have in my custom module, so is giving me an exception because it couldn't found a dependency. Now, I created a .NET Core 2 Console application and did exactly the same, at the time to call containerBuilder.Build() the code jump to the module and I see the services been registered and no exception this time
Why is not loading the Autofac Module in the .NET framework Console App?
PS: I found this blog really useful, I switched the first target framework to .NET 4.61 as you can see in the PropertyGroup but still I'm seeing in grey 4.61 code inside the if.

Small Test
Lets build a sample library with
namespace MyClassLibrary
{
public class Foo
{
public static string Info { get; } = "Conditionals"
#if net461
+ " net461"
#endif
#if NET461
+ " NET461"
#endif
#if NETCORE
+ " NETCORE"
#endif
#if NETSTANDARD
+ " NETSTANDARD"
#endif
#if NETSTANDARD2_0
+ " NETSTANDARD2_0"
#endif
+ "";
}
}
and
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
</PropertyGroup>
</Project>
If we reference this library in a .Net Framework 4.6.1+ console application with a simple
using System;
namespace ConsoleApp.NetCore
{
class Program
{
static void Main( string[] args )
{
Console.WriteLine( MyClassLibrary.Foo.Info );
}
}
}
the output is
Conditionals NET461
github: Complete Solution
Conclusion
The directive net461 is unknown but NET461 is known.
As you can see, size does matters :o)

Related

Could not load type 'Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager' from assembly 'Microsoft.Azure.WebJobs.Host

The application is in Azure Functions,
The error that we are getting from container Pod logs is "Could not load type 'Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager' from assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.26.0".
In our application version all of the dll ver is 3.0.30.0
In the "dll" folder of debug is having the version with 3.0.30.0
And in this version 3.0.30.0, it has the class "Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager"
Not sure, where this "assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.26.0" is coming from.
For me this was happening because Azure Functions Core Tools version mismatched due to upgradation of Visual Studio to latest version.
Removing the Azure Function Tools from the system path C:\Users\user.name\AppData\Local\AzureFunctionsTools and Let Visual Studio automatically install Azure Functions Core Tools fixed the issue.
I had the same issue as below log.
Could not load type 'Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager' from assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.25.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
It was due to my base image for azure functions was old. using the newer base image with below tag(mcr.microsoft.com/azure-functions/dotnet:3.4.2) has fixed my issue.
FROM mcr.microsoft.com/azure-functions/dotnet:3.4.2 AS base
WORKDIR /home/site/wwwroot
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:3.1.416 AS build
WORKDIR /src
This is not direct answer to your question but a tool that would answer it for you. As I had a lot of this kind of errors I have written a helper code to do just that. Its written for the .net framework but with minor changes you can have same thing on core.
//folder where dependencies should be found
var dir = #"C:\Repos\Project\bin";
//dll or exe that you want to inspect
var dll = #"C:\Repos\Project\bin\Project.dll";
var asm = Assembly.ReflectionOnlyLoadFrom(dll);
var stack = new Stack<Data>();
stack.Push(new Data {
ReferencesPath = Array.Empty<Assembly>(),
Assembly = asm
});
List<AssemblyName> visited = new List<AssemblyName>();
while (stack.Any())
{
var current = stack.Pop();
var dependencies = current.Assembly.GetReferencedAssemblies();
visited.Add(current.Assembly.GetName());
foreach (var item in dependencies)
{
if (!visited.Any(x => x.FullName == item.FullName))
{
Assembly dependency;
try
{
dependency = Assembly.ReflectionOnlyLoad(item.FullName);
}
catch
{
var path = Path.Combine(dir, item.Name) + ".dll";
dependency = Assembly.ReflectionOnlyLoadFrom(path);
}
if (dependency.GetName().Version != item.Version)
{
; // put breakpoint here and inspect dependency
// and item when you find your dll in wrong version
// you can inspect current.ReferencesPath to see dependencies
// chain that causes the error
}
stack.Push(new Data
{
Assembly = dependency,
ReferencesPath = current.ReferencesPath.Concat(
new[] { current.Assembly }).ToArray()
});
}
}
}
class Data
{
public Assembly[] ReferencesPath { get; set; }
public Assembly Assembly { get; internal set; }
}

NuGet dependency tree [duplicate]

Is there a way, either textual or graphical, to view the hierarchy of dependencies between NuGet packages?
If you're using the new .csproj, you could get all dependencies with reference in here (after project built):
{ProjectDir}\obj\project.assets.json
Like #neil-barnwell solution, but works with NuGet.Core 2.7+
Install-Package NuGet.Core
Here is the code
using System;
using System.Linq;
using System.Runtime.Versioning;
using System.IO;
using NuGet;
public class Program
{
public static void Main(string[] args)
{
var frameworkName = new FrameworkName(".NETFramework, Version=4.0");
// var packageSource = "https://www.nuget.org/api/v2/";
var packageSource = Path.Combine(Environment.GetEnvironmentVariable("LocalAppData"), "NuGet", "Cache");
var repository = PackageRepositoryFactory.Default.CreateRepository(packageSource);
const bool prerelease = false;
var packages = repository.GetPackages()
.Where(p => prerelease ? p.IsAbsoluteLatestVersion : p.IsLatestVersion)
.Where(p => VersionUtility.IsCompatible(frameworkName, p.GetSupportedFrameworks()));
foreach (IPackage package in packages)
{
GetValue(repository, frameworkName, package, prerelease, 0);
}
Console.WriteLine();
Console.WriteLine("Press Enter...");
Console.ReadLine();
}
private static void GetValue(IPackageRepository repository, FrameworkName frameworkName, IPackage package, bool prerelease, int level)
{
Console.WriteLine("{0}{1}", new string(' ', level * 3), package);
foreach (PackageDependency dependency in package.GetCompatiblePackageDependencies(frameworkName))
{
IPackage subPackage = repository.ResolveDependency(dependency, prerelease, true);
GetValue(repository, frameworkName, subPackage, prerelease, level + 1);
}
}
}
It is also possible to write code against the API in NuGet.Core. Install it via NuGet:
install-package nuget.core
Then you can get a repository object and walk the graph. Here's a sample app I just built:
using System;
using System.Collections.Generic;
using System.Linq;
using NuGet;
namespace ConsoleApplication2
{
class Program
{
static void Main()
{
var repo = new LocalPackageRepository(#"C:\Code\Common\Group\Business-Logic\packages");
IQueryable<IPackage> packages = repo.GetPackages();
OutputGraph(repo, packages, 0);
}
static void OutputGraph(LocalPackageRepository repository, IEnumerable<IPackage> packages, int depth)
{
foreach (IPackage package in packages)
{
Console.WriteLine("{0}{1} v{2}", new string(' ', depth), package.Id, package.Version);
IList<IPackage> dependentPackages = new List<IPackage>();
foreach (var dependency in package.Dependencies)
{
dependentPackages.Add(repository.FindPackage(dependency.Id, dependency.VersionSpec.ToString()));
}
OutputGraph(repository, dependentPackages, depth += 3);
}
}
}
}
In my case, this app outputs something like this:
MyCompany.Castle v1.1.0.3
Castle.Windsor v2.5.3
Castle.Core v2.5.2
MyCompany.Common v1.1.0.6
CommonServiceLocator v1.0
MyCompany.Enum v1.1.0.7
MyCompany.Common v1.1.0.6
CommonServiceLocator v1.0
MyCompany.Enum v1.1.0.7
MyCompany.Enum v1.1.0.7
MyCompany.Versioning v1.3
Castle.Core v2.5.2
Castle.Windsor v2.5.3
Castle.Core v2.5.2
CommonServiceLocator v1.0
NUnit v2.5.10.11092
RhinoMocks v3.6
I've found a nice NPM package to print the dependency tree into console. Of course if you don't mind using/installing NPM/Node.JS.
Considering other solutions, this is the most simple one, you don't need to write your own code or register something, and you get just such dependency tree as you expect. But it works only with packages.config format.
I can't believe this functionality is absent in free Visual Studio editions or nuget.exe too.
I Can Has .NET Core (GitHub repository) produces nice graphs of NuGet dependencies along with a Graphviz representation. And as its name implies, you also get .NET Core compatibility information for free.
If you prefer to run it locally on your computer, I Can Has .NET Core also offers a console mode.
I add a compatible solution with the latest version of nuget-core
install-package nuget.core
This is the console App to get the dependencies graph
class Program
{
static void Main()
{
Console.Write("Enter the local repo folder: ");
var repoFolder = Console.ReadLine();
var repo = new LocalPackageRepository(repoFolder);
IQueryable<IPackage> packages = repo.GetPackages();
OutputGraph(repo, packages, 0);
}
static void OutputGraph(LocalPackageRepository repository, IEnumerable<IPackage> packages, int depth)
{
foreach (IPackage package in packages)
{
Console.WriteLine("{0}{1} v{2}", new string(' ', depth), package.Id, package.Version);
IList<IPackage> dependentPackages = new List<IPackage>();
foreach (var dependencySet in package.DependencySets)
{
foreach (var dependency in dependencySet.Dependencies)
{
var dependentPackage = repository.FindPackage(dependency.Id, dependency.VersionSpec, true, true);
if (dependentPackage != null)
{
dependentPackages.Add(dependentPackage);
}
}
}
OutputGraph(repository, dependentPackages, depth + 3);
}
}
}
Package Visualized from NuGet 1.4 should work. See http://docs.nuget.org/docs/release-notes/nuget-1.4
Since this is an old question, it is important to note the following:
This is a built-in feature in the new csproj format. In Visual Studio 2017 and up, open the Solution Explorer and you can find you packages like:
{Your project}->Dependencies->Packages
You can open each NuGet dependency tree and run with it recursively, effectively seeing not only the dependency tree for specific packages, but also which NuGet packages your project actually installs.
Another option you have is to use the nuget-deps-tree npm package.
It supports both the packages.config format and the newer assets format used by .NET projects.
FYI, MyGet.org has this kind of visualization built-in. You can view dependency graphs on the Feed Details page.
https://github.com/mikehadlow/AsmSpy using this to identify assembly version across a project

How to get the machine configuration filename in .NET Core

I am porting an application from .NET Framework to .NET Core (Standard).
Within the application, we have the following code
public LogMessageListenerFromConfig()
: this(AppDomain.CurrentDomain.BaseDirectory.ConfigurationFile, LoggingSection.DefaultName)
{ }
public LogMessageListenerFromConfig(string section)
: this(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, section)
{ }
public LogMessageListenerFromConfig(string filename, string section)
{
// todo: set up a file watcher and refresh the listeners (etc.) when it changes.
var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = filename };
var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
_section = LoggingSection.Section(configuration, section);
Refresh();
}
This appears to be compatible with .NET Core apart from the following statement
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
There is no SetupInformation on an AppDomain anymore. Fair enough I read that it would cause issues etc. However, how else can I get the application configuration file name in a utility class?
Please be aware that this class will be used in both console and asp.net (core) applications.
Any help appreciated
Stephen
Same problem if you are porting a library to .Net Standard. Solution is this;
install this nuget package system.configuration.configurationmanager then you can use:
var conf = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var configFilePath = conf.FilePath;

Namespace Microsoft.Win32 could not be found

I installed Visual Studio yesterday. I would like to see what version of the .NET Framework is installed with Visual Studio, so I followed this code from Microsoft. (scroll a bit down for the code). I opened a new Visual C# project with 'Console App (.NET Core)' and copied the given code in it.
using System;
using Microsoft.Win32;
public class GetDotNetVersion
{
public static void Main()
{
GetDotNetVersion.Get45PlusFromRegistry();
}
private static void Get45PlusFromRegistry()
{
const string subkey = #"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\";
using (RegistryKey ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(subkey))
{
if (ndpKey != null && ndpKey.GetValue("Release") != null)
{
Console.WriteLine(".NET Framework Version: " + CheckFor45PlusVersion((int)ndpKey.GetValue("Release")));
}
else
{
Console.WriteLine(".NET Framework Version 4.5 or later is not detected.");
}
}
}
// Checking the version using >= will enable forward compatibility.
private static string CheckFor45PlusVersion(int releaseKey)
{
if (releaseKey >= 461808)
return "4.7.2 or later";
if (releaseKey >= 461308)
return "4.7.1";
if (releaseKey >= 460798)
return "4.7";
if (releaseKey >= 394802)
return "4.6.2";
if (releaseKey >= 394254)
return "4.6.1";
if (releaseKey >= 393295)
return "4.6";
if (releaseKey >= 379893)
return "4.5.2";
if (releaseKey >= 378675)
return "4.5.1";
if (releaseKey >= 378389)
return "4.5";
// This code should never execute. A non-null release key should mean
// that 4.5 or later is installed.
return "No 4.5 or later version detected";
}
}
// This example displays output like the following:
// .NET Framework Version: 4.6.1
However, in the editor, the 'RegistryKey' is giving an error: "Namespace cannot be found." The second line 'using Microsoft.Win32;' is grey as if it is not called upon in the code.
When I look in the Solution Explorer panel in Visual Studio, I can list the dependencies. I do not see MicrosoftWin32.dll but I do see mscorlib.dll. I read somewhere that the Microsoft.Win32 namespace could be found there?
Can someone give me some clues as how to resolve this? Where can I find Microsoft.Win32 namespace? Why is it not available in this standard project?
If you use netcore you need to add package Microsoft.Win32.Registry:
Your project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" />
</ItemGroup>
</Project>
Result:

Dynamically load assemblies in ASP.NET 5

I used to have some code which scanned the bin directory of my application for assemblies which weren't loaded in the AppDomain yet and loaded them. It basically looked like:
foreach (var assemblyPath in Directory.GetFiles("path\to\bin", "*.dll"))
{
var inspected = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
Assembly.Load(inspected.GetName());
}
I skipped the try/catch clauses, etc for brevity.
This allowed me to drop assemblies in the bin folder at run-time with implementations for certain interfaces and let the IoC container pick them up automatically. Now with the new Roslyn magic, there are no physical DLL's anymore when debugging. Is there any way to retrieve assembly names, project names or dependency names (in project.json) dynamically.
I guess I have to implement something like this example in the Entropy repo, but I don't know how to implement it for my scenario.
You can use the IAssemblyLoadContextAccessor interface to load ASP.NET 5 class library (.xproj) projects dynamically. The following example code works with Beta 4:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
var assemblyLoadContextAccessor = app.ApplicationServices.GetService<IAssemblyLoadContextAccessor>();
var loadContext = assemblyLoadContextAccessor.Default;
var loadedAssembly = loadContext.Load("NameOfYourLibrary");
}
}
What you are looking for is ILibraryManager implementation which provides access to the complete graph of dependencies for the application. This is already flowed through the ASP.NET 5 DI system. So, you can reach out to it from there.
Sample usage can be found inside RoslynCompilationService.
I solved this issue partly using the ILibraryManager as suggested by #tugberk. I changed the approach a bit which dropped the need of scanning the bin folder for new assemblies. I just want all the loaded assemblies in the current AppDomain.
I injected an instance of the ILibraryManager interface in my type finder class and used the GetReferencingLibraries() method with the name of the core assembly, which is referenced by all the other assemblies in the application.
A sample implementation can be found here, where this is the important part:
public IEnumerable<Assembly> GetLoadedAssemblies()
{
return _libraryManager.GetReferencingLibraries(_coreAssemblyName.Name)
.SelectMany(info => info.Assemblies)
.Select(info => Assembly.Load(new AssemblyName(info.Name)));
}
For .net core users, here is my code for loading assemblies from a specific path. I had to use directives, as it's slightly different for .Net Framework and .Net Core.
In your class header you'll need to declare the using something similar to:
#if NET46
#else
using System.Runtime.Loader;
#endif
And in your function something similar to the following:
string assemblyPath = "c:\temp\assmebly.dll";
#if NET46
Assembly assembly = Assembly.LoadFrom(assemblyPath);
#else
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromAssemblyPath(assemblyPath);
#endif
Its not ASP.NET but it can be converted easily to asp.net.
bellow if function for loading an assembly, and invoke a method inside a class on that assembly.
private static FormCustomized loadLayout(global::System.String layoutFilename, global::System.String layoutNameSpace)
{
FormCustomized mainForm = default;
Type typeMainLayout = default;
FileInfo layoutFile;
layoutFile = new FileInfo(layoutFilename);
layoutFile.Refresh();
if (!layoutFile.Exists)
{
MessageBox.Show("Layout file not found. You need to reinstall the program");
return default;
}
try
{
Assembly assemblyRaw = Assembly.LoadFrom(layoutFilename);
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromAssemblyPath(layoutFilename);
Type typeMainLayoutIni = assembly.GetType(layoutNameSpace + ".initializeLayoutClass");
Object iniClass = Activator.CreateInstance(typeMainLayoutIni, true);
MethodInfo methodInfo = typeMainLayoutIni.GetMethod("AssembliesToLoadAtStart");
enVars.assemblies = (Dictionary<string, Environment.environmentAssembliesClass>)methodInfo.Invoke(iniClass, default);
typeMainLayout = assembly.GetType(layoutNameSpace + ".mainAppLayoutForm");
mainForm = Activator.CreateInstance(typeMainLayout, enVars) as FormCustomized;
}
catch (Exception ex)
{
return default;
}
return default;
}

Categories

Resources