Creating plugin scanner with StructureMap - structuremap

I'm attempting to write a StructureMap plugin scanner for Payment Gateway implementations. I have created an IPaymentGateway interface in an external library. I have created several implementations of IPaymentGateway and put those .dlls in my C:\Extensions\ folder.
Here is my StructureMap configuration:
ObjectFactory.Initialize(cfg =>
{
cfg.Scan(scanner =>
{
scanner.AssembliesFromPath(#"C:\Extensions\");
});
});
Here is my calling code:
var list = ObjectFactory.GetAllInstances<IPaymentGateway>().ToList();
list.ForEach(item => Console.WriteLine(item.FriendlyName));
I would expect that the list should contain each of my implementations of IPaymentGateway, but it doesn't contain anything. What am I missing?
Thanks!

You need to add the types using the scanner:
ObjectFactory.Initialize(cfg => {
cfg.Scan(scanner =>
{
scanner.AssembliesFromPath(#"C:\Extensions\");
scanner.AddAllTypesOf<IPaymentGateway>();
});

Related

remove configuration providers for integration tests in .NET 6

I currently have a web app on .NET 6 with integration tests using WebApplicationFactory.
Here is a simplified version of my Program.cs file:
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
var configuration = builder.Configuration;
services.AddAuthentication()
.AddJwtBearer(o =>
{
var configs = configuration.GetRequiredSection("authOptions");
// using configs here
});
For most services I need to mock, I can remove them from the service descriptor. However, this would not work for services like adding authentication from what I have seen. Basically, I am looking for a way to mock the configurations for integration testing.
The AddJwtBearer also has no access to the IServiceProvider and thus cannot use the injectable IConfiguration. I would like to account for all such features that need to use the configuration from builder.Configuration.
I am trying something like this:
public class CustomWebApplicationFactory: WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration(configBuilder =>
{
// remove all configs or at least user secrets from config builder
// add mocked configurations
});
}
}
I would like to remove the configuration so as to make sure in future if I add a configuration I forget to mock, the integration test would throw an exception instead of using the user secrets.
I suggest the following approach if you would like to clear any existing default configuration providers and then add your custom configuration.
public class CustomWebApplicationFactory: WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration(configBuilder =>
{
//clears all the default configuration providers
configBuilder.Sources.Clear();
//Add your custom configuration from Json file or memory
});
}
}
You can find more examples here.
Hope this helps.

Multiple swagger JSON file generation for different API groups

I have swagger configured in my solution and it is showing up the API documentation properly. Now recently I have developed some new APIs in the same solution and these are also showing up in API documentation and these projects also follow the same naming conventions.
Now my requirement is I want to segregate the new API documentation from the older ones so basically I want two JSON files generated one foe each, old API, AND new API.
My Swagger configuration looks like the following.
Config.EnableSwagger(#"api-docs/{apiVersion}",
c =>
{
c.SingleApiVersion("v1", "SAMPLE API");
c.UseFullTypeNameInSchemaIds();
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
foreach (String x in Directory.GetFiles(Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)), "*.dll")
.Where(f => Path.GetFileNameWithoutExtension(f).ToLower().Contains("sample") ****&& !Path.GetFileNameWithoutExtension(f).ToLower().Contains("sample.api"))****
.Select(f => String.Format(#"{0}\{1}.xml", Path.GetDirectoryName(f), Path.GetFileNameWithoutExtension(f)))
.Where(File.Exists))
{
c.IncludeXmlComments(x);
}
c.OperationFilter<AddRequiredHeaderParameter>();
});
My New API projects are named sample.api.test and old API projects are named sample.web.test.
I added the && part in the where clause to ignore picking my new files in the first JSON doc generation but of no luck. I am new to this and really don't know if it is possible to have two JSON depending on project names. Any help would be greatly appreciated.
I fixed this using IDocumentFilter as follows.
internal class SwaggerFilterOutControllers : IDocumentFilter
{
void IDocumentFilter.Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
var apiKeys = swaggerDoc.paths.Select(s => s.Key).ToList();
foreach (var apiKey in apiKeys)
{
if (swaggerDoc.info.title == "old API" && apiKey.StartsWith("/latapi"))
swaggerDoc.paths.Remove(apiKey);
else if (swaggerDoc.info.title == "New Public API" && !apiKey.StartsWith("/latapi"))
swaggerDoc.paths.Remove(apiKey);
}
}
}
and then in the enableswagger() method i called this filter as
c.DocumentFilter<SwaggerFilterOutControllers>();

Set up Dependency Injection on Service Fabric using default ASP.NET Core DI container

I would like to use ASP.NET Core's default DI container to setup DI for my Service Fabric project.
//This is what I've got so far, and it works great
ServiceRuntime.RegisterServiceAsync(
"MyServiceType",
context => new MyService(context, new MyMonitor()
).GetAwaiter().GetResult();
//This is how I use it
public MyService(StatefulServiceContext context, IMonitor myMonitor)
: base(context)
{
this._myMonitor = myMonitor;
}
How would I set up DI, if MyMonitor class has a dependency on a ConfigProvider class, like this:
public MyMonitor(IConfigProvider configProvider)
{
this._configProvider = configProvider;
}
I think this question will give you some light: Why does ServiceRuntime.RegisterServiceAsync return before the serviceFactory func completes?
Technically, the ServiceRuntime.RegisterServiceAsync() is a dependency registration, it requires you to pass the serviceTypeName and the factory method responsible for creating the services Func<StatelessServiceContext, StatelessService> serviceFactory
The factory method receives the context and returns a service (Stateful or stateless).
For DI, you should register all dependencies in advance and call resolve services to create the constructor, something like:
var provider = new ServiceCollection()
.AddLogging()
.AddSingleton<IFooService, FooService>()
.AddSingleton<IMonitor, MyMonitor>()
.BuildServiceProvider();
ServiceRuntime.RegisterServiceAsync("MyServiceType",
context => new MyService(context, provider.GetService<IMonitor>());
}).GetAwaiter().GetResult();
PS:
Never Register the context (StatelessServiceContext\StatefulServiceContext) in the DI, in a shared process approach, multiple partitions might be hosted on same process and will have multiple contexts.
This code snippet is not tested, I've used in the past, don't have access to validate if matches the same code, but is very close to the approach used, might need some tweaks.
Hi #OscarCabreraRodríguez
I am working on the project that simplifies development of Service Fabric Reliable Services and it has great built-in support for dependency injection scenarios.
You can find general information project page, wiki and specific information about dependency injection here.
The idea is that project abstracts you from working with Service instance directly instead providing you with a set of more concrete objects.
Here is a simple example for ASP.NET Core application:
public static void Main(string[] args)
{
new HostBuilder()
.DefineStatefulService(
serviceBuilder =>
{
serviceBuilder
.UseServiceType("ServiceType")
.DefineAspNetCoreListener(
listenerBuilder =>
{
listenerBuilder
.UseEndpoint("ServiceEndpoint")
.UseUniqueServiceUrlIntegration()
.ConfigureWebHost(
webHostBuilder =>
{
webHostBuilder
.ConfigureServices(
services =>
{
// You can configure as usual.
services.AddTransient<IMyService, MyService>();
})
.UseStartup<Startup>();
});
});
})
.Build()
.Run();
[Route("api")]
public class ApiController : Controller
{
public ApiController(IMyService service) { }
[HttpGet]
[Route("value")]
public string GetValue()
{
return $"Value from {nameof(ApiController)}";
}
}
Hope I understand your use case correctly and this information is relevant.

how to Tell StructureMap to use a specific constructor?

have two services that require an XPathDocument. I want to be able to define named instances of XPathDocumnet to use in the configuration of the two services. I also want to be able to tell StuctureMap which constructor of XPathDocument to use. When I try to get an instance of XPathDocument it tells me that it can't find the plugged type for XmlReader. I want to use the constructor that requires a string uri for the xml file. I cannot seem to get this to work. Here is the StructureMap configuration code.
public class Service1 : IService1 {
public Service1(XPathDocument document) {}
}
public class Service2 : IService2 {
public Service2(XPathDocument document) {}
}
public class Registry1 : Registry {
ForRequestedType<IService1>().TheDefault.Is.OfConcreteType<Service1>()
.CtorDependency<XPathDocument>().Is(x => x.TheInstanceNamed("XPathDocument1"));
ForRequestedType<IService2>().TheDefault.Is.OfConcreteType<Service2>()
.CtorDependency<XPathDocument>().Is(x => x.TheInstanceNamed("XPathDocument2"));
ForRequestedType<XPathDocument>().AddInstances(x => {
x.OfConcreteType<XPathDocument>()
.WithCtorArg("uri").EqualToAppSetting("XmlFile1")
.WithName("XPathDocument1");
x.OfConcreteType<XPathDocument>()
.WithCtorArg("uri").EqualToAppSetting("XmlFile2")
.WithName("XPathDocument2");
});
}
Since XPathDocument is a framework type that is not under your control, you should register it with a factory delegate.
container.Configure(r => r.For<XPathDocument>()
.Use(() => new XPathDocument("XmlFile1"));
But since you need a dofference instance per service, you might be better of not registering this class itself, but only configure your services with a constructor argument where you specify the lambda delegate.

Simple Injector registering IMappingEngine (AutoMapper)

I used Autofac before but now I want give SimpleInjector a try. My problem is, on calling the mappingEngine within my method I get the following error:
Missing type map configuration or unsupported mapping.
Mapping types:
Something -> SomethingDto
Destination path:
IEnumerable`1[0]
Source value:
_mappingEngine.Map<IEnumerable<SomethingDto>>(IEnumerableOfSomething);
^-- doesn't work
Mapper.Map<IEnumerable<SomethingDto>>(IEnumerableOfSomething);
^-- works (That's not what I want)
Mapper.Map is not that what I want. Im registering Automapper based on this here:
Replace Ninject with Simple Injector
container.Register<ITypeMapFactory, TypeMapFactory>();
container.RegisterAll<IObjectMapper>(
MapperRegistry.AllMappers());
container.RegisterSingle<ConfigurationStore>();
container.Register<IConfiguration>(() =>
container.GetInstance<ConfigurationStore>());
container.Register<IConfigurationProvider>(() =>
container.GetInstance<ConfigurationStore>());
container.Register<IMappingEngine, MappingEngine>();
Mapper.Initialize(x =>
{
var profiles = container.GetAllInstances<Profile>();
foreach (var profile in profiles)
{
x.AddProfile(profile);
}
});
Mapper.AssertConfigurationIsValid();
My question ist, how do I register IMappingEngine in SimpleInjector and add my Profiles correctly?
Thanks in advance!
Greets mtrax
Solved! :-)
I registered my profiles too late, after initating the MvcControllerFactory. I hope my pseudo example helps you to prevent such a mistake.
// SimpleInjector
var container = new Container();
// AutoMapper registration
container.Register<ITypeMapFactory, TypeMapFactory>();
container.RegisterCollection(MapperRegistry.Mappers);
container.RegisterSingleton<ConfigurationStore>();
container.Register<IConfiguration>(container.GetInstance<ConfigurationStore>);
container.Register<IConfigurationProvider>(container.GetInstance<ConfigurationStore>);
container.RegisterSingleton(() => Mapper.Engine);
// AutoMapper Profiles registration
container.RegisterCollection<Profile>(new MappingAProfile(),
new MappingBProfile(),
new MappingCProfile());
// Adding AutoMapper profiles
Mapper.Initialize(x =>
{
var profiles = container.GetAllInstances<Profile>();
foreach (var profile in profiles)
{
x.AddProfile(profile);
}
});
Mapper.AssertConfigurationIsValid();
container.Verify();
container.RegisterAsMvcControllerFactory();
*RegisterAsMvcControllerFactory() to find at: Simple Injector MVC Integration Guide.

Resources