Simple Injector and SolrNet - c#

I'm moving from the easy world of Ninject over to the fast world of Simple Injector, but I'm getting stuck with SolrNet. A lot of the popular IoC's have SolrNet integrations already, but not SimpleInjector.
I've started creating my own integration by converting the Unity stuff found at
https://github.com/mausch/SolrNet/tree/master/Unity.SolrNetIntegration
Does anyone either know if this has been done before ( by someone with better knowledge than me) or can anyone convert the below to use simple injector:
private static void RegisterSolrOperations(SolrCore core, IUnityContainer container, bool isNamed = true) {
var ISolrReadOnlyOperations = typeof (ISolrReadOnlyOperations<>).MakeGenericType(core.DocumentType);
var ISolrBasicOperations = typeof (ISolrBasicOperations<>).MakeGenericType(core.DocumentType);
var ISolrOperations = typeof (ISolrOperations<>).MakeGenericType(core.DocumentType);
var SolrServer = typeof (SolrServer<>).MakeGenericType(core.DocumentType);
var registrationId = isNamed ? core.Id : null;
var injectionConstructor = new InjectionConstructor(
new ResolvedParameter(ISolrBasicOperations, registrationId),
new ResolvedParameter(typeof (IReadOnlyMappingManager)),
new ResolvedParameter(typeof (IMappingValidator)));
container.RegisterType(ISolrOperations, SolrServer, registrationId, injectionConstructor);
container.RegisterType(ISolrReadOnlyOperations, SolrServer, registrationId, injectionConstructor);
}
So far I have:
private static void RegisterSolrOperations(SolrCore core, Container container, bool isNamed = true)
{
var ISolrReadOnlyOperations = typeof(ISolrReadOnlyOperations<>).MakeGenericType(core.DocumentType);
var ISolrBasicOperations = typeof(ISolrBasicOperations<>).MakeGenericType(core.DocumentType);
var ISolrOperations = typeof(ISolrOperations<>).MakeGenericType(core.DocumentType);
var SolrServer = typeof(SolrServer<>).MakeGenericType(core.DocumentType);
var registrationId = isNamed ? core.Id : null;
var injectionConstructor = new InjectionConstructor(
new ResolvedParameter(ISolrBasicOperations, registrationId),
new ResolvedParameter(typeof(IReadOnlyMappingManager)),
new ResolvedParameter(typeof(IMappingValidator)));
container.Register(ISolrOperations, SolrServer, registrationId, injectionConstructor);
container.Register(ISolrReadOnlyOperations, SolrServer, registrationId, injectionConstructor);
}
But the injectionConstructor lines are obviously not for simple injector.
Thanks
UPDATE
I should have mention that I was using solrNet 0.4.x so config will be slightly different.
After looking at Steven's answer I now have:
container.RegisterSingle<IReadOnlyMappingManager>(
new MemoizingMappingManager(new AttributesMappingManager()));
container.Register<ISolrDocumentPropertyVisitor, DefaultDocumentVisitor>();
container.Register<ISolrFieldParser, DefaultFieldParser>();
container.RegisterAllOpenGeneric(typeof(ISolrDocumentActivator<>), typeof(SolrDocumentActivator<>));
container.RegisterAllOpenGeneric(typeof(ISolrDocumentResponseParser<>), typeof(SolrDocumentResponseParser<>));
container.Register<ISolrFieldSerializer, DefaultFieldSerializer>();
container.Register<ISolrQuerySerializer, DefaultQuerySerializer>();
container.Register<ISolrFacetQuerySerializer, DefaultFacetQuerySerializer>();
container.RegisterAllOpenGeneric(typeof(ISolrAbstractResponseParser<>), typeof(DefaultResponseParser<>));
container.Register<ISolrHeaderResponseParser, HeaderResponseParser<string>>();
container.Register<ISolrExtractResponseParser, ExtractResponseParser>();
container.RegisterAll<IValidationRule>(new[] {
typeof(MappedPropertiesIsInSolrSchemaRule),
typeof(RequiredFieldsAreMappedRule),
typeof(UniqueKeyMatchesMappingRule)
});
container.Register<ISolrConnection>(() => new SolrConnection(url));
container.RegisterAllOpenGeneric(typeof(ISolrMoreLikeThisHandlerQueryResultsParser<>), typeof(SolrMoreLikeThisHandlerQueryResultsParser<>));
container.RegisterAllOpenGeneric(typeof(ISolrQueryExecuter<>), typeof(SolrQueryExecuter<>));
container.RegisterAllOpenGeneric(typeof(ISolrDocumentSerializer<>), typeof(SolrDocumentSerializer<>));
container.RegisterAllOpenGeneric(typeof(ISolrBasicOperations<>), typeof(SolrBasicServer<>));
container.RegisterAllOpenGeneric(typeof(ISolrBasicReadOnlyOperations<>), typeof(SolrBasicServer<>));
container.RegisterAllOpenGeneric(typeof(ISolrOperations<>), typeof(SolrServer<>));
container.RegisterAllOpenGeneric(typeof(ISolrReadOnlyOperations<>), typeof(SolrServer<>));
container.Register<ISolrSchemaParser, SolrSchemaParser>();
container.Register<ISolrDIHStatusParser, SolrDIHStatusParser>();
container.Register<IMappingValidator, MappingValidator>();
but with this I get the error:
The constructor of the type MappingValidator contains the parameter of
type IValidationRule[] with name 'rules' that is not registered.
Please ensure IValidationRule[] is registered in the container, or
change the constructor of MappingValidator.

That Unity configuration looks overly complex, the Ninject configuration is much simpler. I'm not sure why that Unity configuration is that complex.
I grabbed the Ninject configuration and translated to Simple Injector. Since I'm not familiar with Solr, it could be that Solr depends a few extra tweaks in the Simple Injector configuration, but I think this will get you going:
// using SimpleInjector;
public static void Configure(Container container, string serverURL)
{
container.RegisterInstance<IReadOnlyMappingManager>(
new MemoizingMappingManager(new AttributesMappingManager()));
container.Register<ISolrDocumentPropertyVisitor, DefaultDocumentVisitor>();
container.Register<ISolrFieldParser, DefaultFieldParser>();
container.Register(typeof(ISolrDocumentActivator<>), typeof(SolrDocumentActivator<>));
container.Register(typeof(ISolrDocumentResponseParser<>), typeof(SolrDocumentResponseParser<>));
container.Register<ISolrFieldSerializer, DefaultFieldSerializer>();
container.Register<ISolrQuerySerializer, DefaultQuerySerializer>();
container.Register<ISolrFacetQuerySerializer, DefaultFacetQuerySerializer>();
container.Collection.Register(typeof(ISolrResponseParser<>), new[] {
typeof(ResultsResponseParser<>),
typeof(HeaderResponseParser<>),
typeof(FacetsResponseParser<>),
typeof(HighlightingResponseParser<>),
typeof(MoreLikeThisResponseParser<>),
typeof(SpellCheckResponseParser<>),
typeof(StatsResponseParser<>),
typeof(CollapseResponseParser<>)
});
container.Register<ISolrHeaderResponseParser, HeaderResponseParser<string>>();
container.Collection.Register<IValidationRule>(new[] {
typeof(MappedPropertiesIsInSolrSchemaRule),
typeof(RequiredFieldsAreMappedRule),
typeof(UniqueKeyMatchesMappingRule)
});
container.RegisterInstance<ISolrConnection>(new SolrConnection(this.serverURL));
container.Register(typeof(ISolrQueryResultParser<>), typeof(SolrQueryResultParser<>));
container.Register(typeof(ISolrQueryExecuter<>), typeof(SolrQueryExecuter<>));
container.Register(typeof(ISolrDocumentSerializer<>), typeof(SolrDocumentSerializer<>));
container.Register(typeof(ISolrBasicOperations<>), typeof(SolrBasicServer<>));
container.Register(typeof(ISolrBasicReadOnlyOperations<>), typeof(SolrBasicServer<>));
container.Register(typeof(ISolrOperations<>), typeof(SolrServer<>));
container.Register(typeof(ISolrReadOnlyOperations<>), typeof(SolrServer<>));
container.Register<ISolrSchemaParser, SolrSchemaParser>();
container.Register<IMappingValidator, MappingValidator>();
}

Related

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.

Sparx EA.App doesn't reflect Activator.CreateInstance properly

I've got .NET Framework 4.6.2 console app that is accessing Sparx Enterprise Architect repository. The code below shows a simple example of using Activator.CreateInstance(). The problem is that when creating a second instance the current one (stored in eaApp) is used, so I cannot access two different repositories. Is there any witted solution to this problem?
static void Main(string[] args)
{
EA.App eaApp = (EA.App)Activator.CreateInstance(Type.GetTypeFromProgID("EA.App", true));
EA.App eaApp2 = (EA.App)Activator.CreateInstance(Type.GetTypeFromProgID("EA.App", true));
eaApp.Repository.OpenFile(#"c:\Temp\UCI2.EAP");
eaApp2.Repository.OpenFile(#"c:\Temp\UCI3.EAP");
EA.Element test = eaApp.Repository.GetElementByGuid("{53F2ADAE-E8AC-40da-A06F-D64F525B87E8}");
EA.Element test2 = eaApp2.Repository.GetElementByGuid("{DBF0459F-0662-4e5b-B7E3-A065087B624E}");
Console.WriteLine($"test1: {test.Notes} test2: {test2.Notes}");
Console.ReadKey();
}
If you want to create a new instance do something like this
EA.Repository r = new EA.Repository();
r.OpenFile("c:\\eatest.eap");
As described in the manual
Connecting to the (first) running instance can be done like this:
using System.Runtime.InteropServices;
...
object obj = Marshal.GetActiveObject("EA.App");
var eaApp = obj as EA.App;
var myRepository = eaApp?.Repository;

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)

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?

c# create an instance of an object from string

I have a string variable contain:
string classCode = "public class Person { public string Name{get;set;} }";
How can I create an instance of an object from the classCode ?
like
object obj = CreateAnInstanceAnObject(classCode);
You'll need to use CodeDom to compile an in-memory assembly, and then use reflection to create the type.
Here's a sample article on MSDN that walks through the process of code generation.
Once you've compiled the code, you can use Activator.CreateInstance to create an instance of it.
Building on the answers from above, here is a working demo to generate, compile and instantiate a class from an in-memory assembly:
namespace DynamicCompilation
{
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
internal static class Program
{
private static void Main()
{
var ccu = new CodeCompileUnit();
var cns = new CodeNamespace("Aesop.Demo");
cns.Imports.Add(new CodeNamespaceImport("System"));
var ctd = new CodeTypeDeclaration("Test")
{
TypeAttributes = TypeAttributes.Public
};
var ctre = new CodeTypeReferenceExpression("Console");
var cmie = new CodeMethodInvokeExpression(ctre, "WriteLine", new CodePrimitiveExpression("Hello World!"));
var cmm = new CodeMemberMethod
{
Name = "Hello",
Attributes = MemberAttributes.Public
};
cmm.Statements.Add(cmie);
ctd.Members.Add(cmm);
cns.Types.Add(ctd);
ccu.Namespaces.Add(cns);
var provider = new CSharpCodeProvider();
var parameters = new CompilerParameters
{
CompilerOptions = "/target:library /optimize",
GenerateExecutable = false,
GenerateInMemory = true
};
////parameters.ReferencedAssemblies.Add("System.dll");
var results = provider.CompileAssemblyFromDom(parameters, ccu);
if (results.Errors.Count == 0)
{
var t = results.CompiledAssembly.GetType("Aesop.Demo.Test");
var inst = results.CompiledAssembly.CreateInstance("Aesop.Demo.Test");
t.InvokeMember("Hello", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, inst, null);
}
Console.ReadLine();
}
}
}
Simple put you cannot do this in one line as you are attempting. It is possible to create an instance of an existing class via it's name and one of the overloads of Activator.CreateInstance.
What you are trying to achieve here though is quite different. You are attempting to both 1) define a new class type and 2) create an instance of it. Defining new metadata in the running process dynamically is very difficult to achieve with static languages like C#. It requires a significant amount of work that can't easily be put into a StackOverflow answer.
The following project should guide you in what your trying to accomplish:
RunTime Code Compilation
However, if you are attempting to write code at runtime, you may want to rethink your architecture. You may be creating more of a headache for yourself than you need to be.
What are you trying to accomplish by creating this object?

Categories