Resolving DryIoc serviceKey implementations using a factory class - dryioc

I'm attempting to build a Factory class to return a given service implementation based on an Enum service key.
My registrations:
private static void RegisterPageMap(IRegistrator container)
{
RegisterPage<SchedulePage>(container, PageKey.Schedule);
RegisterPage<AccountsPage>(container, PageKey.Accounts);
RegisterPage<AccountDetailsPage>(container, PageKey.AccountDetails, Reuse.Transient);
// and more
}
private static void RegisterPage<TPage>(IRegistrator builder, PageKey key) where TPage : Page => RegisterPage<TPage>(builder, key, Reuse.Singleton);
private static void RegisterPage<TPage>(IRegistrator builder, PageKey key, IReuse reuse) where TPage : Page
{
builder.Register<TPage>(reuse,
serviceKey: key,
setup: reuse == Reuse.Singleton
? null
: Setup.With(openResolutionScope: true,
allowDisposableTransient: true));
}
The factory class is intended to resolve all of the Pages within the constructor and store them in a backing field for future use.
public sealed class PageFactory : IPageFactory
{
/// <summary>
/// DryIoc will resolve all keyed pages into the constructor in the format of a <see cref="KeyValuePair{PageKey, Page}"/>
/// </summary>
public PageFactory(KeyValuePair<PageKey, Page>[] pages) => _pages = pages;
/// <inheritdoc cref="IPageFactory"/>
public Page GetPageForKey(PageKey key) =>
_pages.First(x => x.Key == key)
.Value;
private readonly KeyValuePair<PageKey, Page>[] _pages;
}
The problem is that when I land at the line _pages = pages, the value being injected has Count = 0 and I'm not sure what I'm missing.
What do I need to do to implement this pattern in DryIoc?

From the first glance , you are registering your pages via implementation type TPage but resolving the dictionary of the Page.
You need to change your registrations to container.Register<Page, TPage>(...).

Related

Manually add custom route parameter to Swagger docs with Swashbuckle.AspNetCore

So I have encountered an interesting case, where I needed to generate swagger documentation for an REST API whose only documentation was an actual document (no inline XML documentation) and which I do not have direct source code access. So I simple wrote a wrapper controller, and overrid each route as such:
[HttpGet("this/{that}/the/{other}")]
public override IActionResult GetWhatever(String that, String other) => base.GetWhatever(that, other);
and then just documented it with standard summary tags, etc. However, one of the now overridden methods uses a querystring internally, and is not exposed as a parameter with [FromQuery], so it is not able to be auto-documented reflectively (and putting in a tag for it without the actual parameter present does not generate documentation for it)
I need a way to add this parameter documentation manually, but through code somehow (not just by adding it to the swagger.json file). I though I could use SwaggerGen's ISchemaFilter to add a parameter description to the associated route/method, but so far I'm not having much luck.
Does anyone have an example of doing something like this?
So it appears what I was looking for was IOpertationFilter. Coupling that with a custom Attribute, I was able to create what I needed to manually add a custom parameter to the Swagger documentation on the fly. See all associated code below, please note Schema/PartialSchema have many properties, I only set Type as it was all I needed, other cases might require more.
SwaggerParameterAttribute.cs
using System;
using Microsoft.AspNetCore.Mvc.Filters;
using Swashbuckle.AspNetCore.Swagger;
/// <summary>
/// Types of Swagger parameters
/// </summary>
public enum SwaggerParamType {Body, NonBody};
/// <summary>
/// Attribute to facilitate manually adding a parameter to auto-generated Swagger documentation
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class SwaggerParameterAttribute : ActionFilterAttribute {
/// <summary>
/// Swagger parameter to inject
/// </summary>
public IParameter Parameter { get; set; }
/// <summary>
/// Default constructor
/// </summary>
/// <param name="ParamType">Type of Swagger parameter (Body/NonBody)</param>
/// <param name="Name">Name of the parameter</param>
/// <param name="Type">Primitive type associated with the parameter (int, bool, string, etc.)</param>
/// <param name="In">Location of the parameter (path, query, etc.)</param>
/// <param name="Description">Description of the parameter</param>
/// <param name="Required">Whether the parameter is required or not (true/false)</param>
public SwaggerParameterAttribute(SwaggerParamType ParamType, String Name, String Type, String In, String Description = "", Boolean Required = false){
switch (ParamType) {
case SwaggerParamType.Body:
Parameter = new BodyParameter() { Name = Name, In = In, Description = Description, Required = Required, Schema = new Schema() { Type = Type } };
break;
case SwaggerParamType.NonBody:
Parameter = new NonBodyParameter() { Name = Name, In = In, Description = Description, Required = Required };
((PartialSchema)Parameter).Type = Type;
break;
default:
throw new ArgumentOutOfRangeException("Invalid Swagger parameter type specified.");
}
}
SwaggerOperationFilter.cs
using System;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Controllers;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using Whatever.NameSpace.Your.Attribute.Is.In;
/// <summary>
/// Custom Swagger Operation Filter
/// </summary>
public class SwaggerOperationFilter : IOperationFilter {
public void Apply(Operation operation, OperationFilterContext context) {
//Check for [SwaggerParameter] add defined parameter to the parameter list
foreach (Attribute attribute in ((ControllerActionDescriptor)context.ControllerActionDescriptor).MethodInfo.GetCustomAttributes()) {
if (attribute.GetType() == typeof(SwaggerParameterAttribute)) {
operation.Parameters.Add(((SwaggerParameterAttribute)attribute).Parameter);
}
}
}
}
Startup.cs (Just the swagger operation filter part)
using Swashbuckle.AspNetCore.Swagger;
using Whatever.NameSpace.Your.Filter.Is.In;
public void ConfigureServices(IServiceCollection services) {
services.AddSwaggerGen(options => {
options.OperationFilter<SwaggerOperationFilter>();
}
}
SomeController.cs (Example Usage)
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using Whatever.NameSpace.Your.Attribute.Is.In;
[HttpGet("this/{that}/the/{other}")]
[SwaggerParameter(ParamType: SwaggerParamType.NonBody, Name: "param1", Type: "string", In: "query", Description: "Some description of param1 here")]
[SwaggerParameter(SwaggerParamType.NonBody, "param2", "string", "query", "Some description of param2 here")]
public override IActionResult GetWhatever(String that, String other) => base.GetWhatever(that, other);

How to register IEnumerable<IService> In DryIoc Mvc Controller like Autofac Enumeration (IEnumerable<B>, IList<B>, ICollection<B>)

Test Code With Autofac is Ok,but with DryIoc is error. How to make this work.
public class HomeController : Controller
{
private readonly ITestAppService _testAppService;
private readonly IEnumerable<ITestAppService> _testAppServiceList;
public HomeController(ITestAppService testAppService, IEnumerable<ITestAppService> testAppServiceList)
{
_testAppService = testAppService;
_testAppServiceList = testAppServiceList;
}
}
public class Test1AppService : ITestAppService{}
public class Test2AppService : ITestAppService{}
public interface ITestAppService: IAppService{}
The error 'Make sure that the controller has a parameterless public constructor ' is caused by field ITestAppService in controller not the field IEnumerable.Following is my register code.
var impls =
typeof(IAppService).Assembly
.GetTypes()
.Where(type =>
type.IsPublic &&
!type.IsAbstract &&
type.GetInterfaces().Length != 0 && typeof(IAppService).IsAssignableFrom(type));
foreach (var type in impls)
{
container.Register(type.GetInterfaces().First(), type, Reuse.Transient);
}
Resolved by dadhi's suggestion,My container is
DryIoc.IContainer container = new DryIoc.Container(
rules =>
{
return rules.WithFactorySelector(Rules.SelectLastRegisteredFactory())
.WithResolveIEnumerableAsLazyEnumerable();
}
);
The dryioc wiki about Resolving from multiple default services may need some update. Rules.SelectLastRegisteredFactory is a method.
The problem is that consdering two implementations of ITestAppService container doesn't know which to select for the first dependency. But you may explicitly define the convention:
var container = new Container(rules => rules
.WithFactorySelector(Rules.SelectLastRegisteredFactory));

Registering simple types in a specific example

Consider the following
ClassA has a constructor that takes an instance of MasterClass and a string and exposes a property called Names of type string[].
ClassB has a constructor that takes an IJuicePresser and a IEnumerable<string>.
ClassC has a constructor that takes an IEnumerable<string>.
Manually I would do something like this to tie them together.
var masterClass = new MasterClass();
var juicePresser = JuicePresser.Create("default");
var classA = new ClassA(masterClass, "string");
var names = classA.Names;
var classB = new ClassB(juicePresser, names as IEnumerable<string>);
var classC = new ClassC(Array.Reverse(names));
How can I set up DryIoc to handle these registrations/resolutions for me?
It might be better to move all these run-time names from constructors to corresponding methods. But here is the matching DryIoc setup as-is:
Live on .NET Fiddle
using System;
using System.Collections.Generic;
using System.Linq;
using DryIoc;
public class Program
{
public static void Main()
{
var c = new Container();
c.Register<MasterClass>();
c.Register<JuicePresser>(Made.Of(() => JuicePresser.Create("default")));
// an example how to inject a primitive value: "string" in this case
c.Register<ClassA>(made: Parameters.Of.Type<string>(_ => "string"));
// service key is optional, just to distinguish the list of strings for consumer.
c.Register<string[]>(
Made.Of(_ => ServiceInfo.Of<ClassA>(), factory => factory.Names),
serviceKey: "names");
// register reverse names using ReverseHelper method
c.Register<string[]>(
Made.Of(() => ReverseHelper(Arg.Of<string[]>("names"))),
serviceKey: "reverse-names");
// specify the names and required type (string[]) for injection
c.Register<ClassB>(made: Parameters.Of.Type<IEnumerable<string>>(typeof(string[]), serviceKey: "names"));
// specify reverse names for injection
c.Register<ClassC>(made: Parameters.Of.Type<string[]>(serviceKey: "reverse-names"));
var classB = c.Resolve<ClassB>();
var classC = c.Resolve<ClassC>();
Console.WriteLine(string.Join(" - ", classB.Names.ToArray()));
// outputs: a - string - z
Console.WriteLine(string.Join(" - ", classC.Names));
// outputs: z - string - a
}
public static T[] ReverseHelper<T>(T[] target) {
Array.Reverse(target);
return target;
}
public class MasterClass {}
public class JuicePresser
{
public readonly string Name;
private JuicePresser(string name) { Name = name; }
public static JuicePresser Create(string name)
{
return new JuicePresser(name);
}
}
public class ClassA
{
public readonly string[] Names;
public ClassA(MasterClass master, string name) {
Names = new[] { "a", name, "z" }; // for example
}
}
public class ClassB
{
public readonly JuicePresser Presser;
public readonly IEnumerable<string> Names;
public ClassB(JuicePresser presser, IEnumerable<string> names) {
Presser = presser;
Names = names;
}
}
public class ClassC
{
public readonly string[] Names;
public ClassC(string[] names) {
Names = names;
}
}
}
Update:
Explaining a bit the part with:
c.Register<string[]>(
Made.Of(_ => ServiceInfo.Of<ClassA>(), factory => factory.Names),
serviceKey: "names");
DryIoc has support for using not only constructors for service creation, but also static and instance methods (factory methods), properties and fields. Here is the wiki topic.
In example above we use Names property of ClassA object as factory method to register service type "string[]" with service key "names".
Let's see in detail:
// Registering service of ClassA. It means that it can be resolved / injected directly,
// or/and we can use it as a factory for resolving further services
c.Register<ClassA>(made: Parameters.Of.Type<string>(_ => "string"));
// Registering service of type "string[]"
c.Register<string[]>(
// Made.Of enables specifying factory method to use for "string[]" resolution, instead default constructor selection rules.
Made.Of(
// 1) Specifying what factory object should be used,
// Here we say to use ClassA service registered in container
requestNotUsedHere => ServiceInfo.Of<ClassA>(),
// 2) Specify that Names property of resolved ClassA object
// should be used for "string[]" resolution
classA => classA.Names),
// As the "string[]" is not very distinctive (unique) service type
// (you might register other "string[]" with different meaning),
// we identify the names with "names" service key. So when injected or
// resolved, you need to specify the service key in addition to type.
serviceKey: "names");
If you want to register with static factory method, property, field, then you don't need to specify request => ServiceInfo.Of<TFactory>() part. BTW, request parameter can be used for conditional selection of factory.

Sitecore Glass data model inheritence

I am using the Glass Mapper on a Sitecore instance where I have a basic data template structure of
Base
BaseWithList
BaseWithExtraContent
BaseWithExtraContentAndCallToActionLink
I have added model classes in my project to follow this structure too. My class names match my template names.
[SitecoreType(TemplateId = "{5D19BD92-799E-4DC1-9A4E-1DDE3AD68DAD}", AutoMap = true)]
public class Base
{
public virtual string Title {get;set;}
public virtual string Content {get;set;}
}
[SitecoreType(TemplateId = "{0491E3D6-EBAA-4E21-B255-80F0607B176D}", AutoMap = true)]
public class BaseWithExtraContent : Base
{
public virtual string ExtraContent {get;set;}
}
[SitecoreType(TemplateId = "{95563412-7A08-46A3-98CB-ABC4796D57D4}", AutoMap = true)]
public class BaseWithExtraContentAndCallToActionLink : BaseWithExtraContent
{
public virtual string CallToActionLink {get;set;}
}
These data models are used from another class that has a list of base type, I want to be able to store any derived type in here so I added attributes as detailed in this tutorial
[SitecoreType(AutoMap = true)]
public class HomePage
{
[SitecoreChildren(InferType = true)]
[SitecoreField(FieldName = "Widgets")]
public virtual IEnumerable<Base> Widgets { get; set; }
}
According to the tutorial this should work. However the list of widget just contains class of the base type.
I then found a later tutorial that said that if you have separated out the models to a different assemblies than the one Glass is installed in you have to add an AttributeConfigurationLoader pointing to the assembly your models are in. The base and derived types are all in the same assembly so I wasn't sure this would solve the issue, but I tried it anyway.
My custom loader config looks like this:
public static class GlassMapperScCustom
{
public static void CastleConfig(IWindsorContainer container)
{
var config = new Config {UseWindsorContructor = true};
container.Install(new SitecoreInstaller(config));
}
public static IConfigurationLoader[] GlassLoaders()
{
var attributes = new AttributeConfigurationLoader("Project.Data");
return new IConfigurationLoader[] {attributes};
}
public static void PostLoad(){
//Remove the comments to activate CodeFist
/* CODE FIRST START
var dbs = Sitecore.Configuration.Factory.GetDatabases();
foreach (var db in dbs)
{
var provider = db.GetDataProviders().FirstOrDefault(x => x is GlassDataProvider) as GlassDataProvider;
if (provider != null)
{
using (new SecurityDisabler())
{
provider.Initialise(db);
}
}
}
* CODE FIRST END
*/
}
}
Upon doing the custom loader config I now get an "Ambiguous match found" exception. I have checked to see if there are any other non Glass attributes set in the classes in that assembly and there aren't.
Any ideas? I guess there are 2 questions.
Why does using the inferred type attribute not load the correct types and only the base types?
Why when I attempt to solve this by adding a custom attribute loader do I get the exception?
Widgets property has two attributes - it's either mapped to the children elements of the item, or a field, can't be both.

Why is AutoFixture Customization causing inherited properties to not be filled?

I wrote the following customization and have it applied as part of a composite on most of my tests. My entities have a read-only Id, but I'm using their SetId method in this customization to make sure all entities have some Id if they are transient (don't have an Id already).
public class SetEntityIdCustomization : ICustomization {
public void Customize(IFixture fixture) {
var engine = ((Fixture)fixture).Engine;
fixture.Customizations.Add(new Postprocessor(
engine, o => {
var entity = o as BaseEntity;
if (entity == null || !entity.IsTransient()) {
return;
}
entity.SetId(fixture.CreateAnonymous<Guid>());
}));
}
}
This has been working great, until I discovered a very odd thing today. If I feed a test one of my entities that directly inherits from BaseEntity, all is well and it's writeable properties are auto-filled. However, if I ask for an entity that inherits from something further down from BaseEntity, my customization prevents the properties from auto-filling.
The User entity in this test method is filled properly:
public class User : BaseEntity {
public string Email { get; set; }
public int CoolThings { get; set; }
}
...
[Theory, AutoDomainData]
public void SomeTest(User user, ...) {
// user.Email and user.CoolThings have auto-filled values, as expected.
...
}
However, the AwesomeUser entity in the following test does not get any of the same properties auto-filled.
public class AwesomeUser : User {
...
}
...
[Theory, AutoDomainData]
public void SomeOtherTest(AwesomeUser user, ...) {
// user.Email nor user.CoolThings have auto-filled values. What gives?
...
}
In both test cases, the Id property is auto-filled because of my customization. If I remove my customization, the SomeOtherTest's AwesomeUser instance gets its inherited properties auto-filled just fine. I must assume that my customization is what is messing things up.
Is there a better way to get all my BaseEntity instances to set their Id, or is there something else I'm missing with AutoFixture? I've applied my customization first, in the middle, and last, to no avail.
The solution provided above is a pretty clever attempt, but not something I've seen before. A more idiomatic solution would be something like this:
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(
new FilteringSpecimenBuilder(
new Postprocessor(
new BaseEntityBuilder(
new ConstructorInvoker(
new ModestConstructorQuery())),
new AutoPropertiesCommand().Execute),
new BaseEntitySpecification()));
}
private class BaseEntityBuilder : ISpecimenBuilder
{
private readonly ISpecimenBuilder builder;
private readonly IRequestSpecification specification;
public BaseEntityBuilder(ISpecimenBuilder builder)
{
this.builder = builder;
this.specification = new BaseEntitySpecification();
}
public object Create(object request, ISpecimenContext context)
{
if (!this.specification.IsSatisfiedBy(request))
return new NoSpecimen(request);
var b = (BaseEntity)this.builder.Create(request, context);
b.SetId((Guid)context.Resolve(typeof(Guid)));
return b;
}
}
private class BaseEntitySpecification : IRequestSpecification
{
public bool IsSatisfiedBy(object request)
{
var t = request as Type;
if (t == null)
return false;
if (!typeof(BaseEntity).IsAssignableFrom(t))
return false;
return true;
}
}
As you can see, this isn't a simple one-liner, which is indicative of AutoFixture being a rather opinionated library. In this case, AutoFixture's opinion is:
Favor object composition over class inheritance.
-Design Patterns, p. 20
AutoFixture is first and foremost a TDD tool, and one of the main advantages of TDD is that it provides feedback about class design. In this case, the feedback is: Inheritance is awkward and troublesome. Reconsider the design.