How to fix Composition Exception in Shellbootstrapper? - c#

I am trying to deploy a new software application and I am getting an exception in startup that I am not getting in debug mode in my office. The application does not start but works fine on my PC.
I tried grabbing any inner exception message but none exists. I am unable to determine what the illegal character is or where it came from.
I am using the Caliburn.Micro framework, which I love and have not had this type of issue before.
This application is running on a machine control PC running Windows Embedded (an XP version - I am running Win7) so possibly it could be related to a .net framework versioning issue? The target framework is 4.7.1 and that is loaded up on the machine PC. Regedit shows Version 4.8.03761 and 528049 on the release element of the v4 Full hive. This matches my own PC setup.
This is pretty much boilerplate caliburn.micro shellbootstrapper code with a log4net config activation thrown in. I have used this code dozens of times with nary a problem!
Any advice on where to look for the problem would be most welcome!
Cam
protected override void Configure()
{
container = new CompositionContainer(new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(container);
container.Compose(batch);
dynamic aPath = AppDomain.CurrentDomain.BaseDirectory;
dynamic ConfigurationFilePath = System.IO.Path.Combine(aPath, "log4net.config.xml");
if (System.IO.File.Exists(ConfigurationFilePath))
{
dynamic ConfigurationFile = new System.IO.FileInfo(ConfigurationFilePath);
log4net.Config.XmlConfigurator.ConfigureAndWatch(ConfigurationFile);
}
}
protected override object GetInstance(Type serviceType, string key)
{
try
{
log.Info("sType:" + serviceType + " key:" + key);
string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
log.Info("contract:" + contract);
var exports = container.GetExportedValues<object>(contract);
if (exports.Any())
return exports.First();
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
catch (CompositionException cx)
{
log.Error("Composition Error in ShellBootStrapper: " + cx.Errors.Count + " msg: "+ cx.Message + "\nIE: " + cx.InnerException);
return null;
}
}
The exception text reads as follows:
Composition Error in ShellBootStrapper: 1 msg:The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.
1) Illegal characters in path.
Resulting in: An exception occurred while trying to create an instance of type 'FDL.ShellViewModel'.
Resulting in: Cannot activate part 'FDL.ShellViewModel'.
Element: FDL.ShellViewModel --> FDL.ShellViewModel --> AssemblyCatalog (Assembly="FDL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
Resulting in: Cannot get export 'FDL.ShellViewModel (ContractName="FDL.ShellViewModel")' from part 'FDL.ShellViewModel'.
Element: FDL.ShellViewModel (ContractName="FDL.ShellViewModel") --> FDL.ShellViewModel --> AssemblyCatalog (Assembly="FDL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
//---------------------------------------------------------------------------
So, now I am embarrassed. My father-in-law went into the hospital and things got hectic. After things calmed down, I (forgetting that I posted this question) put some logging into the bootstrapper to see what the illegal characters were. What I found was that the bootstrapper was loading the ShellView and the ShellView was loading the correct UserControl and the UserControl had a setting in it that had the illegal characters!
I appreciate your willingness to help with this but it is all working now. I found my mistake.
Thank you Frenchy and Jack for your comments!
//----------------------------------------------------------------------------

This is from an older Bootstrapper I used awhile back. It should still work with CM, I know it works on .NetCore 3.0, 4.7.2 & 4.8.
The Container
private CompositionContainer Container;
The Constructor:
public Bootstrapper()
{
Initialize();
LogManager.GetLog = type => new DebugLog(type);
}
Configure:
protected override void Configure()
{
var Catalog =
new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
Container = new CompositionContainer(Catalog);
var Batch = new CompositionBatch();
Batch.AddExportedValue<IWindowManager>(new WindowManager());
Batch.AddExportedValue<IEventAggregator>(new EventAggregator());
Batch.AddExportedValue(Container);
Batch.AddExportedValue(Catalog);
Container.Compose(Batch);
}
GetInstance:
protected override object GetInstance(Type ServiceType, string key)
{
var Contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(ServiceType) : key;
var Exports = Container.GetExportedValues<object>(Contract);
if (Exports.Any()) return Exports.First();
throw new Exception($"Could not locate any instances of contract: {Contract}.");
}
Build Up:
protected override void BuildUp(object instance)
{
Container.SatisfyImportsOnce(instance);
}
GetAllInstances:
protected override IEnumerable<object> GetAllInstances(Type ServiceType)
{
return Container.GetExportedValues<object>(AttributedModelServices.GetContractName(ServiceType));
}
Then Finally The Startup:
protected override async void OnStartup(object sender, StartupEventArgs e)
{
await DisplayRootViewForAsync(typeof(IShell)).ConfigureAwait(false);
}
The Shell Should Look Something Like This
[Export(typeof(IShell))]
public class ShellViewModel : Screen, IShell
{
//Code...
}

Related

Loading and then Unloading DLL with dependency locks the DLL

Loading DLL with dependency dynamically and then unloading it, still lock the DLL and I am unable to delete/replace the dll.
As part of writing a plugin app i'm dynamically load DLL (which has dependency e.g Newtonsoft.Json), run the loaded assembly and then unload it. after unloading,I can't delete the DLL from disk (until I restart my app), BUT, if I use DLL which doesn't have dependencies, it works fine, and not lock the file.
The implementation is based on .NET core 3 load/unload taken from:
https://learn.microsoft.com/en-us/dotnet/standard/assembly/unloadability
I use the AssemblyLoadContext which has a resolver, e.g:
class TestAssemblyLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public TestAssemblyLoadContext(string mainAssemblyToLoadPath) : base(isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(mainAssemblyToLoadPath);
}
protected override Assembly Load(AssemblyName name)
{
string assemblyPath = _resolver.ResolveAssemblyToPath(name);
if (assemblyPath != null)
{
return LoadFromAssemblyPath(assemblyPath);
}
return null;
}
}
and the code that create the context:
[MethodImpl(MethodImplOptions.NoInlining)]
public static void runCommands(string pluginPath, bool execute,out WeakReference alcWeakRef)
{
string pluginLocation = getPath(pluginPath);
PluginLoadContext loadContext = new PluginLoadContext(pluginLocation);
alcWeakRef = new WeakReference(loadContext, trackResurrection: true);
Assembly pluginAssembly = loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(pluginLocation)));
var commands = CreateCommands(pluginAssembly).ToList();
if (execute) {
Console.WriteLine("Commands: ");
foreach (ICommand command in commands)
{
Console.WriteLine($"executing... {command.Execute()}");
}
}
commands.Clear();
loadContext.Unload();
}
I wander if this is something I'm doing wrong, I already tried loading the file from stream e.g:
using (var fs = new FileStream(pluginLocation, FileMode.Open, FileAccess.Read))
{
var pluginAssembly = loadContext.LoadFromStream(fs);
....
....
}
The problem solved, Basically when unloading DLL, you can't do it if you have a Newtonsoft.Json dependency, because they have a bug which locks the file.
This is based on response from github issue I opened

.net reload assembly at runtime

I searched a lot about reloading an assembly at runtime in .NET. The only method I can find is using another AppDomain. But this makes things really complicated. And it is almost impossible in my case because the classes in the assembly which is going to be loaded at runtime do not inherit from MarshalByRefObject. I've looked at Unity game engine. The editor builds the components at runtime and just uses the compiled assembly. How is it possible?
I have done this using MEF. I am not sure if it is an option for you, but it works well. However even with MEF it is somewhat complicated.
On my case I am loading all the dll from a particular folder.
These are the setup classes.
public static class SandBox
{
public static AppDomain CreateSandboxDomain(string name, string path, SecurityZone zone)
{
string fullDirectory = Path.GetFullPath(path);
string cachePath = Path.Combine(fullDirectory, "ShadowCopyCache");
string pluginPath = Path.Combine(fullDirectory, "Plugins");
if (!Directory.Exists(cachePath))
Directory.CreateDirectory(cachePath);
if (!Directory.Exists(pluginPath))
Directory.CreateDirectory(pluginPath);
AppDomainSetup setup = new AppDomainSetup
{
ApplicationBase = fullDirectory,
CachePath = cachePath,
ShadowCopyDirectories = pluginPath,
ShadowCopyFiles = "true"
};
Evidence evidence = new Evidence();
evidence.AddHostEvidence(new Zone(zone));
PermissionSet permissions = SecurityManager.GetStandardSandbox(evidence);
return AppDomain.CreateDomain(name, evidence, setup, permissions);
}
}
public class Runner : MarshalByRefObject
{
private CompositionContainer _container;
private DirectoryCatalog _directoryCatalog;
private readonly AggregateCatalog _catalog = new AggregateCatalog();
public bool CanExport<T>()
{
T result = _container.GetExportedValueOrDefault<T>();
return result != null;
}
public void Recompose()
{
_directoryCatalog.Refresh();
_container.ComposeParts(_directoryCatalog.Parts);
}
public void RunAction(Action codeToExecute)
{
MefBase.Container = _container;
codeToExecute.Invoke();
}
public void CreateMefContainer()
{
RegistrationBuilder regBuilder = new RegistrationBuilder();
string pluginPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
_directoryCatalog = new DirectoryCatalog(pluginPath, regBuilder);
_catalog.Catalogs.Add(_directoryCatalog);
_container = new CompositionContainer(_catalog, true);
_container.ComposeExportedValue(_container);
Console.WriteLine("exports in AppDomain {0}", AppDomain.CurrentDomain.FriendlyName);
}
}
Here is the actual code.
AppDomain domain = SandBox.CreateSandboxDomain($"Sandbox Domain_{currentCount}", directoryName, SecurityZone.MyComputer);
foreach (FileInfo dll in currentDlls)
{
string path = Path.GetFullPath(Path.Combine(directoryName, dll.Name));
if (!File.Exists(path))
File.Copy(dll.FullName, Path.Combine(directoryName, dll.Name), true);
domain.Load(typeof(Runner).Assembly.FullName);
}
You can get the domain back by doing this.
Runner runner = (Runner) domain.CreateInstanceAndUnwrap(typeof(Runner).Assembly.FullName, typeof(Runner).FullName);
runner.CreateMefContainer(); // or runner.Recompose();
You will need to call your code like this.
runner.RunAction(() =>
{
IRepository export = MefBase.Resolve<IRepository>();
export?.Get("123");
Console.WriteLine("Executing {0}", export);
});
Generally speaking, you cannot reload assembly within the same AppDomain. You can create one dynamically and load it (and it will sit in your AppDomain forever), you can load another almost-but-not-quite-the-same copy of your assembly, but once the assembly is in AppDomain, it's stuck.
Imagine that a library assembly defines SomeType and your client code just created an instance. If you unload the library, what is supposed to happen with this instance? If the library is in another AppDomain, the client will use a proxy with a well-defined (in MarshalByRefObject) behaviour (to go zomby with the domain unload and throw exceptions foreverafter). Supporting unloading of arbitrary types would have made the runtime incredibly complicated, unpredictable or both.
As for Unity, see this discussion. Quote:
An "assembly reaload" sounds like some kind of quick update check but in fact the whole scripting environment reloads. This will destroy everything in the managed land. Unity can recover from this by using it's serialization system. Unity serializes the whole scene before the reload, then recreates everything and deserializing the whold scene. Of course only things which can be serialized will "survive" this process.

UWP - How to use Resources.resw in class library

in library
public string GetName()
{
ResourceLoader rl = ResourceLoader.GetForCurrentView("ClassLibrary1/Resources");
return rl.GetString("Name");
}
at "ResourceLoader rl = ResourceLoader.GetForCurrentView("ClassLibrary1/Resources");"
An exception of type 'System.Runtime.InteropServices.COMException' occurred in ClassLibrary1.dll but was not handled in user code
WinRT information: 未找到 ResourceMap。
Additional information: 未找到 ResourceMap。
未找到 ResourceMap。
If there is a handler for this exception, the program may be safely continued.
if i add reference this library , it is working well.
but i Dynamic reference this library ,it is failure.
Assembly assembly = Assembly.Load(new AssemblyName("ClassLibrary1"));
if (assembly == null)
{
return;
}
Type type = assembly.GetType("ClassLibrary1.Class1");
ICore core = Activator.CreateInstance(type) as ICore;
textBlock1.Text = core.GetName();
//ClassLibrary1.Class1 c1 = new ClassLibrary1.Class1();
//textBlock1.Text = c1.GetName(); //it is working well
how to use resources for Dynamic reference in the library?
if i add reference this library , it is working well. but i Dynamic reference this library ,it is failure.
Most of your code is right, you didn't mention what exactly the exception is. Here is my demo:
Portable class library, class1:
public class Class1
{
public Class1()
{
Debug.WriteLine("ClassLib Loaded!");
}
public void Output()
{
Debug.WriteLine("ClassLib method!");
}
}
In UWP app:
Assembly assembly = Assembly.Load(new AssemblyName("ClassLibrary1"));
if (assembly == null)
{
return;
}
Type type = assembly.GetType("ClassLibrary1.Class1");
object obj = Activator.CreateInstance(type);
var method = type.GetMethod("Output");
method.Invoke(obj, null);
The output in the immediate window is like this:
To do this, you will need to:
build your class library.
right click your class lib, choose "Open folder in File Explorer", in the folder find the "bin" folder => "Debug", copy the ClassLibrary1.dll into your UWP project like this:
Although doing this can solve the problem, but in my personal opinion, it seems not easier than directly adding reference of this library.

Unable to cast object of type XXXXXX to type IXXXXX (.NET 4.0)

Can someone please explain what is happening to me? I have a test project that tests a dummy instance of my service. In the test project, I simply reference the dummyService.exe and System.SystemProcess dll.
In my dummyService project however, I have referenced the class library, which itself uses other dlls from other componentsn as well as other projects in my solution.
The problem is that when I run my test, exceptions get thrown( First Chance exceptions for dlls which are loaded and working in the dummyService), in addition invalidcast exception (error message below).
Unable to cast object of type 'Export.CaseOutputGenerator' to type 'Export.ICaseOutputGenerator'.
System.InvalidCastException was caught
Message=Unable to cast object of type 'Export.CaseOutputProcess.CustomCaseOutputGenerator' to type
'Export.CaseOutputProcess.ICaseOutputGenerator'.
Source=Export.CaseOutputProcess
StackTrace:
at Export.CaseOutputProcess.CaseOutputGeneratoryFactory.GetCaseOutputGeneratorObject(String assemblyName, String className)
in C:\Monitor\Export.CaseOutputProcess\CaseOutputGeneratoryFactory.cs:line 56
at Monitor.BOMock.GenerateCaseOutput(String OutputFolder, String iFile, Int32 seqNum, DataTable CaseSettings, String SettingFileName)
in C:\Monitor\BOMock\BOMock.cs:line 1069
at Monitor.BOMock.Handling() in C:\Monitor\BOMock\BOMock.cs:line 492 InnerException:
public static ICaseOutputGenerator GetCaseOutputGeneratorObject(string assemblyName, string className)
{
ICaseOutputGenerator customeOutputGen = null;
var obj = GetObject(assemblyName, className);
if (obj != null)
caseOutputGen = (ICaseOutputGenerator)obj; // FAILS HERE
return caseOutputGen;
}
private static object GetObject(string fullName, string className)
{
try
{
Type caseOutputGen = null;
var localAssembly = Assembly.LoadFrom(fullName);
foreach (var testType in localAssembly.GetTypes())
{
if (!testType.FullName.EndsWith(className, StringComparison.InvariantCultureIgnoreCase)) continue;
caseOutputGen = testType;
break;
}
if (caseOutputGen == null) return null;
var obj = Activator.CreateInstance(caseOutputGen);
return obj;
}
catch (FileNotFoundException ex)
{
throw new Exception("Failed to load assembly: " + Environment.NewLine + fullName, ex);
}
catch (Exception ex)
{
throw new Exception("Failed to load assembly: " + Environment.NewLine + fullName, ex);
}
}
Where assemblyName is the Path to the dll file to load and className happens to be the name of the class to create an instance of.
In the code, as you see, using reflection I load the assembly at the assemblyName PATH provided (String assemblyName) http://msdn.microsoft.com/en-us/library/system.reflection.assembly.loadfrom.aspx , and then using reflection again, I then create an instance of the className (String className ) contained in the loaded assembly. http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx
How do I remedy this problem please? I don't want to have to reference all my dlls in the test project. How do I get around or solve this problem please?? Thanks in advance.
Based on that stack trace, it looks like the assembly where the type lives is not being found. If you just add the reference to the compiled exe, you're probably not going to get the other libraries along with it. I think you've got a couple of choices:
Go ahead and bite the bullet: add the references to the other libraries in your test project. They're typically not transitive: just because your service knows about them doesn't necessarily follow that your test's assembly knows about them as well.
Add a post-compilation step to your test's project that copies over the other assemblies so that they can be found by the app domain running your test.
Use dependency injection and an inversion of control container. There are quite a few out there, but Castle Windsor, StructureMap and Unity come to mind. Scott Hanselman's got a great list of them on his blog: http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx

mefcontrib interceptingCatalog export error

I am trying to test mef and mefcontrib in asp.net mvc2 app but i got an error:
Cannot cast the underlying exported value of type LoggerExtSys.Domain.WebLogger
(ContractName="LoggerExtSys.Domain.IWebLogger") to type LoggerExtSys.Domain.IWebLogger.
My test project here
code in Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
var catalog = new CatalogBuilder()
.ForAssembliesInDirectory(HttpRuntime.BinDirectory, "*ExtSys.dll")
.Build();
// Create interception configuration
var cfg = new InterceptionConfiguration()
.AddInterceptor(new StartableStrategy());
// Create the InterceptingCatalog with above configuration
var interceptingCatalog = new InterceptingCatalog(catalog, cfg);
// Create the container
var container = new CompositionContainer(interceptingCatalog);
// exception here
var barPart = container.GetExportedValue<IWebLogger>();
barPart.Debug("Test");
}
Exception when i try to get GetExportedValue
code in WebLogger:
[Export(typeof(IWebLogger))]
public class WebLogger : IWebLogger
{
#region IWebLogger Members
public void Debug(string str)
{
}
#endregion
#region ICoreExtension Members
public void Initialize()
{
}
#endregion
}
But in desktop app all working good.
How to fix it? Thanks for all
Ok, the problem was in code block which load assemblies:
public AggregateCatalog ForAssembliesInDirectory(string directory, string pattern)
{
IList<ComposablePartCatalog> _catalogs = new List<ComposablePartCatalog>();
var dir = new DirectoryInfo(directory);
Assembly assembly;
foreach (var file in dir.GetFiles(pattern))
{
assembly = Assembly.LoadFile(file.FullName);
_catalogs.Add(new AssemblyCatalog(assembly));
}
return new AggregateCatalog(_catalogs);
}
After all test i remove it and use DirectoryCatalog. I dont know why but its work in desktop and web app.
Who will tell me why my old code not working in web app will get accepted answer and 50 bounty. Thanks for all
I think the problem is either here:
[Export(typeof(IWebLogger))]
public class WebLogger : IWebLogger
{
or in the way you handle type referencing and resolution.
I would try to change the line:
var barPart = container.GetExportedValue<IWebLogger>();
into:
var barPart = container.GetExportedValue<WebLogger>();
or you can also try to always use fully qualified names so not only IWebLogger but put its full namespace before.
you say this works well in windows based application, what assemblies did you reference in that project or how do you write in there the content of your Application_Start event handler? Are you sure it's absolutely the same?

Categories