So I'm using Ninject, specifically the contextual binding as follows :
Bind<IBlah>().ToMethod(x => FirstBlahProvider.Instance.GiveMeOne()).WhenTargetHas<FirstAttribute>().InRequestScope();
Bind<IBlah>().ToMethod(x => SecondBlahProvider.Instance.GiveMeOne()).WhenTargetHas<SecondAttribute>().InRequestScope();
I need to use the Kernel to get a given instance and would like to do it based on the Condition WhenTargetHas<T>. Something like the following would be great.
var myblah = Kernal.Get<IBlah>(x => x.HasWithTarget<FirstAttribute>)
How can you retrieve an instance based on the condition?
Worked out the answer :
Best to avoid using WhenTargetHas<T> instead use WithMetaData(key, value)
So
Bind<IBlah>().ToMethod(x => FirstBlahProvider.Instance.GiveMeOne()).WhenTargetHas<FirstAttribute>().InRequestScope();
Bind<IBlah>().ToMethod(x => SecondBlahProvider.Instance.GiveMeOne()).WhenTargetHas<SecondAttribute>().InRequestScope();
Becomes :
Bind<IBlah>().ToMethod(x => FirstBlahProvider.Instance.GiveMeOne()).WithMetaData("Provider", "First);
Bind<IBlah>().ToMethod(x => SecondBlahProvider.Instance.GiveMeOne()).WithMetaData("Provider", "Second");
You then need to create an Attribute which inherits the Ninject ConstraintAttribute and use that attribute in your constructor arguement.
As :
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)]
public class FirstProviderConstraint : ConstraintAttribute
{
public override bool Matches(IBindingMetadata metadata)
{
return metadata.Has("Provider") && metadata.Get<string>("Provider") == "First";
}
}
You then use it in a constructor arg as :
public class Consumer([FirstProviderConstraint] IBlah)
{
...
}
Or resolving from the Kernel
Get<ISession>(metaData => metaData.Get<string>(BindingKeys.Database) == BindingValues.OperationsDatabase)
I need to resolve scoping but that's how you satisfy both Constructor injection and explicit resolution from the Kernel when you have more than one binding.
Related
I have some validation classes like:
public class AnimalValidator : AbstractValidator<AnimalDTO>
{
// ...
}
And I would like to automatically register only the ones that implement an IAutomaticValidation interface to be used to validate DTOs received by the controllers.
I tried doing:
builder.Services.AddController( /* ... */ ).AddFluentValidation(options =>
{
options.RegisterValidatorsFromAssemblyContaining<IAutomaticValidation>();
});
But it still looks for the AbstractValidator class instead.
Managed to do it with:
options.RegisterValidatorsFromAssemblyContaining<MyEntityValidator>
(
x => x.ValidatorType.GetInterfaces().Any(x => x == typeof(IAutomaticValidation))
);
But then decided to use annotations instead:
options.RegisterValidatorsFromAssemblyContaining<MyEntityValidator>
(
x => x.ValidatorType.CustomAttributes
.Any(x => x.AttributeType == typeof(AutomaticValidationAction))
);
I am developing a web application using Laravel 8. I have noticed that quite a lot of things have changed in Laravel 8 including factories.
I have a factory class MenuCategoryFactory for my MenuCategory model class with the following definition.
<?php
namespace Database\Factories;
use App\Models\Menu;
use App\Models\MenuCategory;
use Illuminate\Database\Eloquent\Factories\Factory;
class MenuCategoryFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = MenuCategory::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'name' => $this->faker->name,
'menu_id' => Menu::factory()->create(),
];
}
}
In my code (database seeder class), I am trying to override the menu_id as follow while I am creating the factories.
$restaurant = Restaurant::first();
MenuCategory::factory()->create([
'menu_id' => $restaurant->menu->id
]);
But it is not using the value I passed, $restaurant->menu->id. Instead, it is creating a new menu. What is wrong missing in my code and how can I fix it?
In your factory definition, don't call ->create(), instead set it up like this:
public function definition()
{
return [
'name' => $this->faker->name,
'menu_id' => Menu::factory(),
];
}
Then you you should be able to set up related models (assuming you the relationships setup in the model) like this:
$restaurant = Restaurant::first();
MenuCategory::factory()->for($restaurant)->create();
Change the definition to
public function definition()
{
return [
'name' => $this->faker->name,
'menu_id' => function() {
Menu::factory()->create()->id,
}
];
}
then you can replace the value
I have a constructor such as:
public AnalyticsController(ClassA classA, ClassB classB, bool isLiveEnvironment)
{
...
}
isLiveEnvironment is determined using a call to an existing static class such as:
MultiTenancyDetection.GetInstance().IsLive();
I would like to be able to make this call outside of the controller and inject the result into isLiveEnvironment. Is this possible? I can not see how this can be done.
You can accomplish this using WithConstructorArgument and using a callback:
kernel.Bind<AnalyticsController>()
.ToSelf()
.WithConstructorArgument("isLiveEnvironment", ctx => MultiTenancyDetection.GetInstance().IsLive() );
You can even achieve this more generally (but i would not really recommend binding such a generic type for such a specific use case):
IBindingRoot.Bind<bool>().ToMethod(x => MultiTenancyDetection.GetInstance().IsLive())
.When(x => x.Target.Name == "isLiveEnvironment");
Alternatively, if you need the same configuration value in several / a lot of classes, create an interface for it:
public interface IEnvironment
{
bool IsLive { get; }
}
internal class Environment : IEnvironment
{
public bool IsLive
{
get
{
return MultiTenancyDetection.GetInstance().IsLive();
}
}
}
IBindingRoot.Bind<IEnvironment>().To<Environment>();
I am trying to setup some mappings and am getting this exception:
Cannot extend unmapped class: CommonEntity
[MappingException: Cannot extend unmapped class: CommonEntity]
NHibernate.Cfg.XmlHbmBinding.ClassBinder.GetSuperclass(String
extendsName) +217
NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.AddEntitiesMappings(HbmMapping
mappingSchema, IDictionary`2 inheritedMetas) +352
NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.Bind(HbmMapping
mappingSchema) +85
NHibernate.Cfg.Configuration.AddDeserializedMapping(HbmMapping
mappingDocument, String documentFileName) +156
I have 3 classes. Entity, CommonEntity and User. Theres no entity or commonentity table, only a User table. User inherits from CommonEntity and CommonEntity inherits from Entity. Entity and CommonEntity are abstract.
I have defined this mapping:
public class Mapping : ConventionModelMapper
{
public Mapping()
{
IsRootEntity((type, declared) =>
{
return typeof(Entity<Guid>) == type.BaseType;
});
IsEntity((x,y) => typeof(Entity<Guid>).IsAssignableFrom(x) && !x.IsAbstract && !x.IsInterface);
Class<Entity<Guid>>(x =>
{
x.Id(c => c.Id, m=>m.Generator(Generators.GuidComb));
x.Version(c=>c.Version, (vm) => { });
});
}
}
Which is used like this:
var types = typeof(Mapping).Assembly.GetExportedTypes().Where(t => typeof(Entity<Guid>).IsAssignableFrom(t));
var mapping = new Mapping().CompileMappingFor(types);
configuration.AddMapping(mapping);
Both User and CommonEntity are in the "types" array. I have tried adding a mapping for CommonEntity too but it made no difference.
Class<CommonEntity>(x =>
{
x.Property(c => c.DateCreated, m => m.Type<UtcDateTimeType>());
x.Property(c => c.DateModified, m => m.Type<UtcDateTimeType>());
});
Also tried calling Subclass instead of Class. If i inherit User directly from Entity everything works fine. Any help?
The problem appears to have been that CommonEntity was meeting the requirement for IsRootEntity. I modified it like so and things seem to be working now.
IsRootEntity((type, declared) =>
{
return !type.IsAbstract &&
new[] {typeof (Entity<Guid>), typeof (CommonEntity)}.Contains(type.BaseType);
});
Using Autofac, I would like to register a component and specify for a specific dependency to be resolved to a named instance.
I found samples like the following using constructor injection which is almost what I want.
builder.Register(c => new ObjectContainer(ConnectionStrings.CustomerDB))
.As<IObjectContainer>()
.Named("CustomerObjectContainer");
builder.Register(c => new ObjectContainer(ConnectionStrings.FooDB))
.As<IObjectContainer>()
.Named("FooObjectContainer");
builder.Register(c => new CustomerRepository(
c.Resolve<IObjectContainer>("CustomerObjectContainer"));
builder.Register(c => new FooRepository(
c.Resolve<IObjectContainer>("FooObjectContainer"));
However, I need this with property injection and I don't want to specify all dependencies.
Something like:
builder.Register<CustomerRepository>().With<IObjectContainer>("CustomerObjectContainer");
builder.Register<FooRepository>().With<IObjectContainer>("FooObjectContainer");
The build up of all unspecified dependnecies should happen with unnamed instances.
Thanks,
Alex
[ADDITION TO THE ANSWER FROM Danielg]
An overload to resolve by type for any property of that type.
public static IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> WithDependency<TLimit, TReflectionActivatorData, TStyle, TProperty>(
this IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> registration,
Func<IComponentContext, TProperty> valueProvider)
where TReflectionActivatorData : ReflectionActivatorData
{
return registration.WithProperty(new ResolvedParameter((p, c) =>
{
PropertyInfo prop;
return p.TryGetDeclaringProperty(out prop) &&
prop.PropertyType == typeof(TProperty);
},
(p, c) => valueProvider(c)));
}
I don't think that autofac has a shorthand way of doing this yet, but it is possible with a little effort.
I've written an extension method that does this. Throw it in a static extension class and you should be fine. The extension also shows how to do this the long way.
public static IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> WithResolvedProperty<TLimit, TReflectionActivatorData, TStyle, TProperty>(
this IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> registration,
string propertyName, Func<IComponentContext, TProperty> valueProvider)
where TReflectionActivatorData : ReflectionActivatorData
{
return registration.WithProperty(new ResolvedParameter((p, c) =>
{
PropertyInfo prop;
return p.TryGetDeclaringProperty(out prop) &&
prop.Name == propertyName;
},
(p, c) => valueProvider(c)));
}
Don't mind the super long method signature, autofac registrations are very verbose.
You can use the extension like this.
builder.RegisterType<Foo>()
.WithResolvedProperty("Bar", c => c.Resolve<IBar>());