With structuremap 2.6.4.1 my container is configured like this:
existingContainer.Configure(expression =>
{
expression.For<IDocumentSession>()
.HybridHttpOrThreadLocalScoped()
.Use(container =>
{
var store = container.GetInstance<IDocumentStore>();
return store.OpenSession();
});
}
HybridHttpOrThreadLocalScoped does not exist in structure map 3 so my question is, what is the equivalent configuration in structuremap 3?
As of StructureMap 3, anything HttpContext related lives within a separate Nuget package called StructureMap.Web which can be found here.
The reason for this is StructureMap 3 is now PLC (Portalble Class Library) compliant, so splitting web-related lifecycles into its own package makes sense.
It is there, says here http://jeremydmiller.com/2014/03/31/structuremap-3-0-is-live/ that is now a Structuremap.Web nuget to add to your project for it to work.
Related
I'm implementing a framework for C# on ASP.NET using Dotnet 6. I want part of the framework to be extensible by outside parties; they just need to implement a few classes and we can integrate their work via Nuget or direct assembly reference.
Part of what they need to complete is a Registration Class so they can define how their engine should be registered with the dependency injection container. This is an example of what I'd expect 3rd parties to supply:
public class EchoServiceRegistration : IRegisterDI
{
public IServiceCollection Register(IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<EchoEngine>();
return serviceCollection;
}
}
In the consuming application, I'm looking for all of the classes that implement the IRegisterDI interface using the AppDomain class, which is a riff off this SO answer https://stackoverflow.com/a/26750/2573109. (Note: I switched to using name-based matching just for troubleshooting and will change it back to a better implementation once properly solved):
List<Type> allRegistrationClasses = AppDomain
.CurrentDomain
.GetAssemblies()
.SelectMany(it => it.GetTypes())
.Where(tp => tp.GetInterfaces()
.Any(inter => inter.Name == nameof(IRegisterDI)))
.ToList();
As written, this returns 0 types.
On the next troubleshooting iteration, I proved that the registration class is available to the caller. So, I manually created an instance of the EchoServiceRegistration class, as seen below. This time, the AppDomain contains a single entry for EchoServiceRegistration (as expected).
var allRegistrationClasses = AppDomain.CurrentDomain.GetAssemblies().SelectMany(it => it.GetTypes()).Where(tp => tp.GetInterfaces().Any(inter => inter.Name == nameof(IRegisterDI))).ToList();
var echoServiceRegistration = new EchoServiceRegistration();
echoServiceRegistration.Register(builder.Services);
if (allRegistrationClasses.Count is not 1) throw new InvalidOperationException(); // Does not throw an exception
To prove I didn't accidentally fix something, I commented out the two lines related to Echo Service Registration, and allRegistrationClasses again contains 0 records.
var allRegistrationClasses = AppDomain.CurrentDomain.GetAssemblies().SelectMany(it => it.GetTypes()).Where(tp => tp.GetInterfaces().Any(inter => inter.Name == nameof(IRegisterDI))).ToList();
// var echoServiceRegistration = new EchoServiceRegistration();
// echoServiceRegistration.Register(builder.Services);
if (allRegistrationClasses.Count is not 1) throw new InvalidOperationException(); // Throws an exception
My gut reaction is I don't understand how the AppDomain determines what assemblies to load. I started reading the documentation Microsoft provides about it, but it seems like a deep topic and I won't have a more clear understanding without quite a bit of dedicated reading.
How do I guarantee the full set of classes are available when calling this during the DI Container build? Please let me know if I can provide any additional detail or clarity.
It looks to me like on your first test, the assembly is not loaded into the app domain because it's not used.
On your second test, you are manually creating an instance so you are forcing the loading of the assembly. Note that even if the assembly is added as a reference, it won't be loaded unless it's actually needed.
You need to explicitly load the assemblies for them to be scanned. One way of doing this would be to have all extension libraries live in a particular folder from which you can then load them before registration:
var files = Directory.GetFiles(#"C:\my-framework\extensions");
foreach (var file in files)
{
var extension = Path.GetExtension(file);
if(extension == ".dll")
{
var asm = Assembly.LoadFile(file);
Console.WriteLine($"Loaded extension [{asm.FullName}]");
}
}
Once the assemblies are loaded, you should get the desired results.
This is an overly-simplified example but it should be enough to get you going.
I'm using AutoFac to automatically register dependencies based on their interface implementations like so:
builder.RegisterAssemblyTypes(Assembly.GetEntryAssembly()).AsImplementedInterfaces();
This works great for the entry assembly, but what about all of the related assemblies?
I'd like to do something like:
IList<Assembly> assemblies = GetLoadedAssemblies();
foreach(var assembly in assemblies)
{
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces();
}
I've searched and see a bunch of .netcore 1.0 stuff with AssemblyLoadContext, etc., but that no longer seems to exist in 1.1. Basically, when you search, you get lots of outdated references to stuff that no longer works.
There's got to be a way to get the currently loaded assemblies.
How can I do this?
In .NET Core 2.0 Lots of APIs were brought from .NET Framework, among them is AppDomain.CurrentDomain.GetAssemblies() which allows you to get the assemblies that have been loaded into the execution context of application domain. Of course the concept wasn't fully ported from .NET Framework to .NET Core so you can't create your own app domains and treat them as unit of isolation inside single process (so you only have one app domain). But this is enough for what you want to achieve.
You can use Scrutor which is working with .netstandard 1.6 or take a look how they're doing it here.
public IImplementationTypeSelector FromAssemblyDependencies(Assembly assembly)
{
Preconditions.NotNull(assembly, nameof(assembly));
var assemblies = new List<Assembly> { assembly };
try
{
var dependencyNames = assembly.GetReferencedAssemblies();
foreach (var dependencyName in dependencyNames)
{
try
{
// Try to load the referenced assembly...
assemblies.Add(Assembly.Load(dependencyName));
}
catch
{
// Failed to load assembly. Skip it.
}
}
return InternalFromAssemblies(assemblies);
}
catch
{
return InternalFromAssemblies(assemblies);
}
}
This is an old question , but none of the answers works for me.
I use autofac with asp.net core webapi project (.net 6).
This is where I need to get assemblies:
host
// autofac
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
// autofac container
.ConfigureContainer<ContainerBuilder>(builder => {
// get assemblies for register
var assemblies = ???
builder.RegisterAssemblyTypes(assemblies).AsImplementedInterfaces()...
});
The project is desinged by DDD, dependcies are like this:
webapi -> domain,infra
infra -> domain
For my situation:
AppDomain.CurrentDomain.GetAssemblies() can only get webapi assembly.
BTW: Assembly.GetEntryAssembly() can get the webapi assebmly too.
webapiAssembly.GetReferencedAssemblies() can only get domain project's assembly. Can't find the infra assembly.
Why GetReferencedAssemblies() doest not get all of the referenced assemblies as expected?
It's because in my project, webapi uses an repository interface, which is defines in domain, but implements in infra. Webapi project doesn't make any direct dependcy with infra layer (use class/interface defined in infra). So the infra assembly is not included.
If webapi has a directly dependcy with infra, use GetReferencedAssemblies() would be just fine.
Here is another solution that works for me:
Use DependencyContext which is inclued in Microsoft.Extensions.DependencyModel (nuget package) to get all assemblies that I wanted (webapi, domain, infra).
public static class AssemblyHelper
{
// get all assemblies that I wanted
// you may want to filter assemblies with a namespace start name
public static Assembly[] GetAllAssemblies(string namespaceStartName = "")
{
var ctx = DependencyContext.Default;
var names = ctx.RuntimeLibraries.SelectMany(rl => rl.GetDefaultAssemblyNames(ctx));
if (!string.IsNullOrWhiteSpace(namespaceStartName))
{
if (namespaceStartName.Substring(namespaceStartName.Length - 1, 1) != ".")
namespaceStartName += ".";
names = names.Where(x => x.FullName != null && x.FullName.StartsWith(namespaceStartName)).ToArray();
}
var assemblies = new List<Assembly>();
foreach (var name in names)
{
try
{
assemblies.Add(Assembly.Load(name));
}
catch { } // skip
}
return assemblies.ToArray();
}
}
I recently started working on a Xamarin Android/iOS project with a PCL class where I want to put all the logic in. Like my Refit interfaces, ReactiveUI ViewModels et cetera, but every time when trying to execute my code I get a error saying that my interface is not a Refit interface. Currently my interface looks like this.
public interface IMyApi
{
[Post("/authenticate")]
IObservable<Models.ApiResponses.AuthenticationResponse> SigninRaw([Body] JObject credentials);
[Get("/service")]
IObservable<Models.ApiResponses.MyListResponse> GetServiceListRaw();
[Get("/service/{id}/idstatus")]
IObservable<Models.ApiResponses.IdResponse> GetIdStatusRaw(string Id);
}
As far as I know this looks good and this also works when I'm trying to load this from a specific platform like iOS project. But when trying to do it from a PCL if fails! I have installed the Refit package in both of my platform specific project Android & iOS and I referenced a dll in the PCL, what did I miss?
If there is need for more information or you have any question, please do not hesitate to ask.
Well without further ado, thank you for reading and hopefully someone can assist me with this, because I starting to loose my mind the past couple of days.
Edit: added calling method.
Here I calling it from a ViewModel
var client = new HttpClient(NetCache.UserInitiated)
{
BaseAddress = new Uri("https://api.address.com")
};
var api = RestService.For<IMyApi>(client); <= here it crashes
var response = api.SigninRaw(token);
I've managed to track this down, there are actually a few issues at play. Fortunately there are ways to work around them.
The first problem is that the PCL interfaces aren't being detected in the first place. Refit runs a utility at compile time (InterfaceStubGenerator) which scans the subfolders for interface classes and generates implementation code for each one. These are all packed into an intermediate file called RestStubs.g.cs which gets included in with the assembly. This utility, however, is only run on the projects that Refit has been added to via nuget, and since that doesn't include PCL projects the interfaces in those projects never get processed. The solution is to call this utility manually in a pre-build step and include the generated file in each of the platform projects. Go to your PCL project's property settings and add the following to the pre-build steps:
..\..\..\..\packages\refit.3.0.1\tools\InterfaceStubGenerator.exe ..\..\..\ProjName.Droid\RefitStubs.cs ..\..\..\ProjName
..\..\..\..\packages\refit.3.0.1\tools\InterfaceStubGenerator.exe ..\..\..\ProjName.iOS\RefitStubs.cs ..\..\..\ProjName
That will generate RefitStubs.cs for your platform projects, so add each file to it's respective project.
Ordinarily that would be the end of it were it not for another problem. The RestService.For<> generics that you call to get the implementations make the assumption that the implementation classes reside in the same assembly as their corresponding interfaces. Obviously that isn't the case for PCL projects. To get around this you need to implement your own version of the RestService class, this will probably serve most of your needs:
public static class PclRestService
{
public static T For<T>(string hostUrl)
{
var className = "AutoGenerated" + typeof(T).Name;
var typeName = typeof(T).Namespace + "." + typeof(T).Name.Replace(typeof(T).Name, className);
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var generatedType = assembly.GetType(typeName);
var requestBuilder = RequestBuilder.ForType<T>();
var client = new HttpClient(new HttpClientHandler()) { BaseAddress = new Uri(hostUrl) };
return (T)Activator.CreateInstance(generatedType, client, requestBuilder);
}
}
You then call it like so:
var netService = PclRestService.For<IMyApi>("http://jsonplaceholder.typicode.com");
var result = await netService.GetDataOrSomething();
One last problem you may encounter is multiple declarations of the PreserveAttribute class, which is declared at the top of the stub files. So long as you don't add Refit interfaces to your platform projects this shouldn't happen, but if it does then you have 3 options:
modify the InterfaceStubGenerator utility to not create that code
write a pre-processor to strip that code out once it has been generated
go to Refit's "refit.targets" file and comment-out the line "<Exec Command="$(RefitExecCmd)" />
The Refit tools folder includes the template file used to generate stub files but for some strange reason it's ignored altogether and statically linked to the application, so editing it in the tools folder doesn't do anything at all.
My MVC project uses Autofac 3.0 and I want to RegisterType by reflection.
First defined one interface and implementation named like IRegisterDemo and RegisterDemo less parameters.
I tried to use builder.RegisterType(typeof(RegisterDemo)).As(typeof(IRegisterDemo)) in my Application_Start method, it successed, but it's not my purpose.
I want to define one attribute like UseAutofacAttribute to reflect the types and use RegisterType(ImplType).As(InterfaceType), when I wrote the code in Application_Start, it worked, so I built another project to do this further and referenced the project dll in my MVC project, and in Application_Start just run one static method, unfortunately it failed.
So I want to know the reason and how to change?
If you want to make your registrations attribute based, I'd suggest you use Autofac's MEF adapter. It will let you decorate your types with the ExportAttribute and be picked up by your ContainerBuilder.
Example
[Export(typeof(IRegisterDemo))] // <-- This exports RegisterDemo as IRegisterDemo
public class RegisterDemo : IRegisterDemo { }
// Example method to register assemblies with ContainerBuilder
public void RegisterPartsFromReferencedAssemblies(ContainerBuilder builder)
{
// Get referenced assemblies
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();
// Create an AssemblyCatalog from each assembly
var assemblyCatalogs = assemblies.Select(x => new AssemblyCatalog(x));
// Combine all AssemblyCatalogs into an AggregateCatalog
var catalog = new AggregateCatalog(assemblyCatalogs);
// Register the catalog with the ContainerBuilder
builder.RegisterComposablePartCatalog(catalog);
}
This example shows how to export a type and how to register referenced assemblies with the ContainerBuilder. You can then do container.Resolve<IRegisterDemo>() to get the exported part (or have it injected into a class).
NOTE: This will register exported parts from all referenced assemblies, including:
Assemblies specified in the assemblies element of the Web.config file
Assemblies built from custom code in the App_Code directory
Assemblies in other top-level folders
I'm somewhat new to .Net, so go easy on me ;-). Anyway.
I working on my first WP7 library project which I hope will be compatible with both XNA and SilverLight applications. Based on whether I'm in XNA or Silverlight one of my factory classes needs to load different config class. Whats the best way to determine this at runtime, from a library.
I know I could do this with the "SILVERLIGHT+WINDOWS_PHONE" preprocessor directives at compile time. But that would mean building two DLLs, which isn't ideal.
~Sniff
I suspect that the information you're looking for can be found in the Environment.Version property or in the OperatingSystem.Version property.
The best I could think of is setting up your library like so:
[Conditional(#XNA),
Conditional(#WINDOWS_PHONE)]
public void DoSomeWork()
{
var x = null;
x = DoSomeXNAWork();
x = DoSomeWP7Work();
if (x != null)
{
...
}
}
[Conditional(#XNA)]
private ?? DoSomeXNAWork()
{
return ??;
}
[Conditional(#WINDOWS_PHONE)]
private ?? DoSomeWP7Work()
{
return ??;
}
Then, just make sure the project referencing this library has the directive set. Kind of like how Microsoft uses the Debug conditional classes such Debug.WriteLine(...). I'm not sure how you could get it to use 2 different config files. I'm sure there is a way because when you create a new Web Project (ASP.NET) there is a config file that is split into Web.Debug.config and Web.Release.config. I couldn't find an answer as to how to do it outside of ASP.NET though.