mefcontrib interceptingCatalog export error - c#

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?

Related

How to fix Composition Exception in Shellbootstrapper?

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...
}

How can I add AutoMapper Profiles to my mapper config using a for loop?

I want to load a bunch of automapper profiles of referenced libraries, without having to type each one out by hand.
I'm trying to take the following steps:
Get all profiles from referenced assemblies
Add profiles to mapper config
Register mapper for DI
Step 1 works, but something goes wrong in step 2.
Current code:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var container = new UnityContainer();
var assemblyNames = Assembly.GetExecutingAssembly().GetReferencedAssemblies()
.Where(a => a.Name.StartsWith("OKL_KPS"));
var assemblies = assemblyNames.Select(an => Assembly.Load(an));
var loadedProfiles = new List<Type>();
foreach (var assembly in assemblies)
{
var assemblyProfiles = assembly.ExportedTypes.Where(type => type.IsSubclassOf(typeof(Profile)));
loadedProfiles.AddRange(assemblyProfiles);
}
var mapconfig = new MapperConfiguration(cfg =>
{
// Magic should happen here
foreach (var profile in loadedProfiles)
{
var resolvedProfile = container.Resolve(profile) as Profile;
cfg.AddProfile(resolvedProfile);
}
});
container.RegisterInstance<IMapper>(mapconfig.CreateMapper());
config.DependencyResolver = new UnityResolver(container);
//routes here
}
}
I also tried cfg.AddProfile((Profile)Activator.CreateInstance(profile.AssemblyQualifiedName, profile.Name).Unwrap());, but this returns the assembly name of the service I'm using it in, not the name of the library where the profile is from.
Edit
The assemblies aren't loading during the register step. To hack this there's a Dummy class in each library which are initialised before registering the profiles. Optimal solution is not needing these dummy classes, otherwise it would be cleaner to add each profile explicitly.
I also tried adding the ExportAttribute to the profile, but that didn't work either.
You scan loaded assemblies on available properties using LINQ queries. Something like this should work:
var profiles = AllClasses.FromLoadedAssemblies().
Where(type => typeof(Profile).IsAssignableFrom(type));
//add profiles to config
var mapconfig = new MapperConfiguration(cfg =>
{
// Magic should happen here
foreach (var profile in profiles)
{
var resolvedProfile = container.Resolve(profile) as Profile;
cfg.AddProfile(resolvedProfile);
}
});
//register mapper using config
container.RegisterInstance<IMapper>(mapconfig.CreateMapper());
You can add profiles by listing your assembly instance, assembly name or type.
Using names:
var mapconfig = new MapperConfiguration(cfg =>
{
cfg.AddProfiles("Foo.YourProject.API");
cfg.AddProfiles("Foo.YourProject.Service");
cfg.AddProfiles("Foo.YourProject.Repository");
...
});
Also check official documentation for more information.
Within the documenation is described on how to use AutoMapper with the Microsoft DI framework. And there it simply forwards to the corresponding NuGet package and an article that describes how it searches through the current application domain for all the open types and profiles to load them.
In your classes you then have to simply inject an IMapper imapper into the constructor which then simply does what you expect.
The only next caveat I ran into was, that not all my assemblies where loaded before I called services.AddAutoMapper() in my ConfigureServices() method. But for this case I simply added a simple helper method, which will be called before DI starts to do its work:
public static void LoadAllLocalAssemblies()
{
var entryAssembly = Assembly.GetEntryAssembly();
var location = entryAssembly.Location;
var path = Path.GetDirectoryName(location);
var files = Directory.EnumerateFiles(path, "*.dll");
foreach (var file in files)
{
try
{
Assembly.LoadFrom(file);
}
catch
{
}
}
}
After that all assemblies are loaded into the current app domain and the NuGet package will resolve all classes that are derived from Profile. My handlers get an IMapper mapper injected within the constructor and within my method I can call mapper.Map<MyDestination>(mySource) and it works as expected.
No dummy classes, interfaces or whatsoever is needed.

How to simulate ConfigurationManager in LINQPad

I'm trying to test some code in LINQPad. However, the base class calls Configuration Manager. How can I simulate that when testing in LINQPad.
void Main()
{
var tRepo = new TestRepository();
var result = tRepo.GetAsync(1);
result.Dump();
}
public partial class TestRepository : BaseRepository<Customer>, ICustomerRepository
{
// Here base throws the errror
public TestRepository() : base("DbConnString")
{
}
}
Here's the constructor for BaseRepository (from a compiled DLL, so not editable in LINQPad):
protected BaseRepository(string connectionStringName)
{
var connectionString = ConfigurationManager.ConnectionStrings[connectionStringName];
Connection = new SqlConnection(connectionString.ConnectionString);
Connection.Open();
}
The answer can be found on the LinqPad website FAQ
http://www.linqpad.net/faq.aspx
I'm referencing a custom assembly that reads settings from an application configuration file (app.config). Where should I put my application config file so that LINQPad queries will pick it up?
Create a file called linqpad.config in the same folder as LINQPad.exe and put your configuration data there. Don't confuse this with linqpad.exe.config:
•linqpad.exe.config is for the LINQPad GUI
•linqpad.config is for your queries.
Something that might be useful for you, I created it some time ago.
This is an extension method, which you can use to force the reload of configuration from specific file. It uses reflection to change the private fields in the manager, clears the configuration and then conditionally reloads it. It is much easier than manually editing the config file of LINQPad.
public static void ForceNewConfigFile(this Type type, bool initialize = true)
{
var path = type.Assembly.Location + ".config";
if (!File.Exists(path))
throw new Exception("Cannot find file " + path + ".");
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", path);
var typeOfConfigManager = typeof(ConfigurationManager);
typeOfConfigManager.GetField("s_initState", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, 0);
typeOfConfigManager.GetField("s_configSystem", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, null);
var typeOfClientConfigPaths = typeOfConfigManager.Assembly.GetTypes().Where(x => x.FullName == "System.Configuration.ClientConfigPaths").Single();
typeOfClientConfigPaths.GetField("s_current", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, null);
if (initialize)
{
var dummy = ConfigurationManager.AppSettings;
}
}
Example usage:
typeof(SomeType).ForceNewConfigFile();
System.Configuration.ConfigurationManager.AppSettings.Dump();
SomeType is just a type contained in the assembly, which will be used as a source for location of the config file. Assumption is: configuration file exists beside the DLL file and is named {Assembly.Location}.config.
I went looking for this as well, but didn't want to create a whole new file, point, have other share that file etc. So I added in the ConfigurationBuilder, created a dictionary and added it to a InMemoryCollection.
#nullable enable
private IConfiguration _config;
void Main()
{
var builder = new ConfigurationBuilder();
var dictonary = new Dictionary<string, string>
{
{"Authentication:SecretKey","SuperSecret"}
};
builder.AddInMemoryCollection(dictonary);
_config = builder.Build();
_config.GetValue<string>("Authentication:SecretKey").Dump();
}
Example

Building programmatically a project

I need to build a project programmatically for a .csproj I am creating on the fly.
While searching Google I found the classes and API provided by the MS for the MSBuild Engine. With that information, I create a process which executes msbuild.exe and then reads the output, but now I want to use the namespace Microsoft.Build.Execution to build the project. This is my program:
public class Compiler
{
private static string locationOfMSBuilldEXE = "";
public static void Build(string msbuildFileName)
{
BuildManager manager = BuildManager.DefaultBuildManager;
ProjectInstance projectInstance = new ProjectInstance(msbuildFileName);
var result = manager.Build(new BuildParameters()
{
DetailedSummary = true
},
new BuildRequestData(projectInstance, new string[] { "Build" }));
var buildResult = result.ResultsByTarget["Build"];
var buildResultItems = buildResult.Items;
string s = "";
}
}
The results show that this is building fine, but I need to know the detailed output from the compile and how to view it. It would be really helpful if someone can give me link to a good tutorial or a book on MSBuild.
Thanks #ritchmelton. Though I figured it out myself.
Here is my code : I have used an in built logger ConsoleLogger
public class Compiler
{
private static string locationOfMSBuilldEXE = "";
public static void Build(string msbuildFileName)
{
ConsoleLogger logger = new ConsoleLogger(LoggerVerbosity.Normal);
BuildManager manager = BuildManager.DefaultBuildManager;
ProjectInstance projectInstance = new ProjectInstance(msbuildFileName);
var result = manager.Build(
new BuildParameters()
{
DetailedSummary = true,
Loggers = new List<ILogger>(){logger}
},
new BuildRequestData(projectInstance, new string[] { "Build" }));
var buildResult = result.ResultsByTarget["Build"];
var buildResultItems = buildResult.Items;
string s = "";
}
}
You need to add a instance of a class that implements the ILogger interface to your BuildParameters. You can add a new instance of one of the supplied loggers in the Microsft.Build.Logging namespace, or you can implement ILogger yourself as it is very small and there is a helper class in the Microsoft.Build.Utilities namespace called Logger that is easy to extend.
Build loggers
ILogger interface
Logger helper
If you just want to build a project or solution, without elaborate parameters, you can do it more simply. Pseudocode:
using namespace Microsoft.Build.Evaluation;
var p = Project.Load("path to project");
p.SetGlobalProperty("Configuration", "Release");
p.Build(...);
That's it! BuildParameters and so forth are for quite advanced scenarios. Visual Studio itself uses them.
Dan (msbuild dev)

compiling/merging dll into standalone exe with wpf

Background : Merging dlls into a single .exe with wpf
How shall i merge a .dll reference into the .exe file, i read the above post, got principle behind it, but i am not able to figure out how to do it?( i am newbie, sorry)
The reference file is HtmlagilityPack.dll
Currently my App.xaml.cs contains :
public partial class App : Application
{
public App(){
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);
// proceed starting app...
}
static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
//We dont' care about System Assembies and so on...
if (!args.Name.ToLower().StartsWith("Html")) return null;
Assembly thisAssembly = Assembly.GetExecutingAssembly();
//Get the Name of the AssemblyFile
var name = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll";
//Load form Embedded Resources - This Function is not called if the Assembly is in the Application Folder
var resources = thisAssembly.GetManifestResourceNames().Where(s => s.EndsWith(name));
if (resources.Count() > 0)
{
var resourceName = resources.First();
using (Stream stream = thisAssembly.GetManifestResourceStream(resourceName))
{
if (stream == null) return null;
var block = new byte[stream.Length];
stream.Read(block, 0, block.Length);
return Assembly.Load(block);
}
}
return null;
}
}
Where else am i supposed to make changes?, i have being trying past an hour with an example of http://blog.mahop.net/post/Merge-WPF-Assemblies.aspx But not able to figure out how to do it with HtmlAgilityPack.
Okay, finally had to use the SmartAssembly program.
But still looking for a solution to do it by code.
Your code looks slightly off, it should look more like this:
public class App : Application
{
[STAThreadAttribute()]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);
// etc...
}
// etc...
You then also need to change the "Startup object" setting in the properties page of your project to use the App class (i.e. the above code) - you should then see the Main method of this class being the first code executed when you start debugging.

Categories