Hi I am using Ninject in a Xamarin MVVM project. What I am trying to do is to bind specific implementations based on a enum type:
var foo = new Ninject.Parameters.Parameter("type", VMType, true);
Kernel.Get<ICommonComponentVM>(foo);
and the provider:
public class ICommonComponentVMProvider : Provider<ICommonComponentVM>
{
protected override ICommonComponentVM CreateInstance(IContext context)
{
//return the implementation based on type
}
}
which is binded in the Kernel Module as:
public class CoreModule : NinjectModule
{
public override void Load()
{
Bind<ICommonComponentVM>().ToProvider<ICommonComponentVMProvider>();
}
}
How can I extract the custom parameter from the binding IContext?
Or is this the correct way to do this? The Ninject wiki lacks this info.
EDIT
I arrived at
var param = context.Parameters.Single((arg) => arg.Name == "type");
but accessing the value of the parameter with param.GetValue needs two arguments: IContext and ITarget. I have the context but what should I put as Target?
In the meantime it works with null:
var type = (CommonVMTypes)param.GetValue(context, null);
so it looks like this:
protected override ICommonComponentVM CreateInstance(IContext context)
{
var param = context.Parameters.Single((arg) => arg.Name == "type");
if (param == null){
return null;
}
var type = (CommonVMTypes)param.GetValue(context, null); //<-- Needs an Action ITarget
switch (type)
// ...
}
You can access the parameters by the property ICollection<IParameter> IContext.Parameters. You can find it by using context.Parameters.Single(x => x.Name == "type").
You could also subclass Parameter or implement IParameter for a custom parameter type with strongly typed information, p.Ex. ComponentVMTypeParameter and then select it using context.Parameters.OfType<ComponentVMTypeParameter>().Single().
Alternative approaches:
use conditional bindings (When(...) syntax, can check for parameters, too) instead of the providers. Does not require extending the provider.
use a factory instead of a provider.
using named bindings:
Bind<IFoo>().To<Foo1>().Named("Foo1")
IResolutionRoot.Get<IFoo>("Foo1");
However, in principle there's no need to use an IProvider. You could instead
However, if there's a limited amount of types I'd consider using an abstract factory instead
Related
Documentation Says : The model for that context is cached and is for all further instances of the context in the app domain. This caching can be disabled by setting the ModelCaching property on the given ModelBuidler
But i can't find way to do it. I have to disable caching because I am adding Model at runtime and loading all the models from assembly and creating database.
I found this link which says one way of achieving this is using DBModelBuilding - adding model mannually to context but it is for Entity Framework, Not helped for EF Core.
Entity Framework 6. Disable ModelCaching
I hope some one has solution for this.
Thank you
Once a model is successfully created, EF Core will cache it forever, unless you implement a cache manager that is able to tell whether a model is equivalent to another, and therefore it can be cached or not.
The entry point is to implement the cache manager:
internal sealed class MyModelCacheKeyFactory : IModelCacheKeyFactory
{
public object Create([NotNull] DbContext context)
{
return GetKey(context);
}
}
The GetKey method which you have to write must return an object that will be used as key. This method should inspect the provided context and return the same key when the models are the same, and something different when they are not. More on IModelCacheKeyFactory Interface.
I understand, this might not be clear (and it wasn't for me either), so I write a full example of what I have in production.
A Working Example
My target is to use the same context for different schemas. What we need to do is
create a new context option
implement the logic in the context
create the cache key factory
make the extension method to specify the schema
call the extension method on the db context
1. Create a new context option
Here there is a boilerplate containing _schemaName only. The boilerplate is necessary as the extension option is immutable by design and we need to preserve the contract.
internal class MySchemaOptionsExtension : IDbContextOptionsExtension
{
private DbContextOptionsExtensionInfo? _info;
private string _schemaName = string.Empty;
public MySchemaOptionsExtension()
{
}
protected MySchemaOptionsExtension(MySchemaOptionsExtension copyFrom)
{
_schemaName = copyFrom._schemaName;
}
public virtual DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this);
public virtual string SchemaName => _schemaName;
public virtual void ApplyServices(IServiceCollection services)
{
// not used
}
public virtual void Validate(IDbContextOptions options)
{
// always ok
}
public virtual MySchemaOptionsExtension WithSchemaName(string schemaName)
{
var clone = Clone();
clone._schemaName = schemaName;
return clone;
}
protected virtual MySchemaOptionsExtension Clone() => new(this);
private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
private const long ExtensionHashCode = 741; // this value has chosen has nobody else is using it
private string? _logFragment;
public ExtensionInfo(IDbContextOptionsExtension extension) : base(extension)
{
}
private new MySchemaOptionsExtension Extension => (MySchemaOptionsExtension)base.Extension;
public override bool IsDatabaseProvider => false;
public override string LogFragment => _logFragment ??= $"using schema {Extension.SchemaName}";
public override long GetServiceProviderHashCode() => ExtensionHashCode;
public override void PopulateDebugInfo([NotNull] IDictionary<string, string> debugInfo)
{
debugInfo["MySchema:" + nameof(DbContextOptionsBuilderExtensions.UseMySchema)] = (ExtensionHashCode).ToString(CultureInfo.InvariantCulture);
}
}
}
2. The logic in the context
Here we force the schema to all the real entities. The schema is obtained by the option attached to the context
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var options = this.GetService<IDbContextOptions>().FindExtension<MySchemaOptionsExtension>();
if (options == null)
{
// nothing to apply, this is a supported scenario.
return;
}
var schema = options.SchemaName;
foreach (var item in modelBuilder.Model.GetEntityTypes())
{
if (item.ClrType != null)
item.SetSchema(schema);
}
}
3. Create the cache key factory
Here we need to the create the cache factory which will tel EF Core that it can cache all the models on the same context, i.e. all the contexts with the same schema will use the same model:
internal sealed class MyModelCacheKeyFactory : IModelCacheKeyFactory
{
public object Create([NotNull] DbContext context)
{
const string defaultSchema = "dbo";
var extension = context.GetService<IDbContextOptions>().FindExtension<MySchemaOptionsExtension>();
string schema;
if (extension == null)
schema = defaultSchema;
else
schema = extension.SchemaName;
if (string.IsNullOrWhiteSpace(schema))
schema = defaultSchema;
// ** this is the magic **
return (context.GetType(), schema.ToUpperInvariant());
}
}
The magic is here is in this line
return (context.GetType(), schema.ToUpperInvariant());
that we return a tuple with the type of our context and the schema. The hash of a tuple combines the hash of each entry, therefore the type and schema name are the logical discriminator here. When they match, the model is reused; when they do not, a new model is created and then cached.
4. Make the extension method
The extension method simply hides the addition of the option and the replacement of the cache service.
public static DbContextOptionsBuilder UseMySchema(this DbContextOptionsBuilder optionsBuilder, string schemaName)
{
if (optionsBuilder == null)
throw new ArgumentNullException(nameof(optionsBuilder));
if (string.IsNullOrEmpty(schemaName))
throw new ArgumentNullException(nameof(schemaName));
var extension = optionsBuilder.Options.FindExtension<MySchemaOptionsExtension>() ?? new MySchemaOptionsExtension();
extension = extension.WithSchemaName(schemaName);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
optionsBuilder.ReplaceService<IModelCacheKeyFactory, MyModelCacheKeyFactory>();
return optionsBuilder;
}
In particular, the following line applies our cache manager:
optionsBuilder.ReplaceService<IModelCacheKeyFactory, MyModelCacheKeyFactory>();
5. Call the extension method
You can manually create the context as follows:
var options = new DbContextOptionsBuilder<DataContext>();
options.UseMySchema("schema1")
options.UseSqlServer("connection string omitted");
var context = new DataContext(options.Options)
Alternatively, you can use IDbContextFactory with dependency injection. More on IDbContextFactory Interface.
You'll need to change the cache key to properly represent the model that you are building/make it distinct.
Implement IDbModelCacheKeyProvider Interface on derived DbContext. Check this out
https://learn.microsoft.com/en-us/dotnet/api/system.data.entity.infrastructure.idbmodelcachekeyprovider?redirectedfrom=MSDN&view=entity-framework-6.2.0
Build the model outside the DbContext and then provide it in the options.
I am using Autofac for IoC in my project. Due to some legacy software libraries I must pass some services to the controller that can't be resolved, and must be passed as parameter.
I've made a generic control using delegate factories like this:
public MyClass<TController, TInterface> {
private delegate TController ControllerFactory(TInterface service);
protected TController _myController;
protected TController Controller {
get
{
return _controller
?? (_controller = ServiceLocator.Resolve<ControllerFactory>()
.Invoke(this);
}
}
}
This works perfect, but for this to work I need the controller's service parameter name and the delegate service parameter name be the same, because as I have read, Autofac pairs the parameter BY NAME !!
I've seen you can do it by type registering the class with generic Func<>, but due to the legacy app I would need to leave "clean" registrations i.e.:
containerBuilder.RegisterType<MyController>();
Does anyone know if it's possible to make the delegate match the parameter by type??
Does anyone know if it's possible to make the delegate match the parameter by type??
Yes, you can use predefined delegates. See dynamic instantiation section here.
Here's an quick example:
public class ComponentFactory
{
private readonly Func<Dependency, Component> _componentFactory;
public ComponentFactory(Func<Dependency, Component> componentFactory)
{
_componentFactory = componentFactory;
}
public Component Create(Dependency dependency)
{
return _componentFactory(dependency);
}
}
public class Component
{
private readonly Dependency _dependency;
public Component(Dependency dependency)
{
_dependency = dependency;
}
}
public class Dependency
{
}
Registration + Usage
var builder= new ContainerBuilder();
builder.RegisterType<ComponentFactory>();
builder.RegisterType<Component>();
builder.RegisterType<Dependency>();
var container = builder.Build();
var factory = container.Resolve<ComponentFactory>();
//Usage with typed parameters
var component = factory.Create(new Dependency());
**Be warned, if you use this method, Autofac throws an exception if you try to add parameters with of the same type. Ex. Component has two dependencies on Dependency
Exception looks something like this:
The input parameter type list
has duplicate types. Try registering a custom delegate type instead of
using a generic Func relationship.
Autofac is more specific about what type you register the controller as than most DI containers. It will only resolve the type by its type if you include .AsSelf() in the registration of the controller. Here is a module we use in our project for registering MVC controllers.
public class MvcModule
: Module
{
protected override void Load(ContainerBuilder builder)
{
var currentAssembly = typeof(MvcModule).Assembly;
builder.RegisterAssemblyTypes(currentAssembly)
.Where(t => typeof(IController).IsAssignableFrom(t))
.AsImplementedInterfaces()
.AsSelf()
.InstancePerDependency();
}
}
Using this registration, you can resolve each controller by controller type.
var type = typeof(HomeController);
var controller = container.Resolve(type);
I have defined ISpecimenBuilder for my models and use it like that:
new Fixture().Customize(new ModelCustomization());
I want to use it in most of my tests concerning model. I also want to apply some form of post-processing in one of my test classes. Specifically I want to fill property CompanyHistory of all created Offers. It feels like it could be done like that:
fixture.Build<Offer>()
.With(o => o.CompanyHistory, _previouslyCreatedCompanyHistory)
.Create();
But Build<T> disables all customizations and I need them.
Can I do something like that?
fixture.Build<Offer>()
.WithCustomization(new ModelCustomization()) // there is no such method, but i'd like it to be
.With(o => o.CompanyHistory, _previouslyCreatedCompanyHistory)
.Create();
Or should I write my own Behavior? If so, can someone provide me with guidelines on doing that?
EDIT:
I feel I have to stress out that I want to use both my common customization (ModelCustomization) and Postprocessor
EDIT 2:
What I meant from the beginning is that ModelCustomization can (and should) create Offer and my to-be postprocessor should use that already created specimen and fill some of its properties.
Here is how you can create and use a Postprocessor in this case:
[Fact]
public void Test()
{
var fixture = new Fixture();
// (You may also include other customizations here.)
fixture.Customizations.Add(
new FilteringSpecimenBuilder(
new Postprocessor(
new MethodInvoker(
new ModestConstructorQuery()),
new OfferFiller()),
new OfferSpecification()));
var offer = fixture.Create<Offer>();
// -> offer.CompanyHistory has the value supplied in OfferFiller command.
}
The OfferFiller command is defined as:
internal class OfferFiller : ISpecimenCommand
{
public void Execute(object specimen, ISpecimenContext context)
{
if (specimen == null)
throw new ArgumentNullException("specimen");
if (context == null)
throw new ArgumentNullException("context");
var offer = specimen as Offer;
if (offer == null)
throw new ArgumentException(
"The specimen must be an instance of Offer.",
"specimen");
Array.ForEach(offer.GetType().GetProperties(), x =>
{
if (x.Name == "CompanyHistory ")
x.SetValue(offer, /*value*/);
else
x.SetValue(offer, context.Resolve(x.PropertyType));
});
}
}
The OfferSpecification is defined as:
internal class OfferSpecification : IRequestSpecification
{
public bool IsSatisfiedBy(object request)
{
var requestType = request as Type;
if (requestType == null)
return false;
return typeof(Offer).IsAssignableFrom(requestType);
}
}
I had a similar problem and have tried solutions mentioned here, but they didn't work as expected. Finally, I've found an implementation of a PostProcessWhereIsACustomization class, that does exactly what I needed:
AutoFixture customization to allow insertion of arbitrary postprocessing logic a la Customize( c=>c.Do()) but in a global manner Revised for v3 (initally for v2)
May save somebody some Googling.
I ended up writing following Customization:
private class OfferWithCompanyModelCustomization: ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new FilteringSpecimenBuilder(new Postprocessor(
new ModelSpecimenBuilder(), new FillModelPropertiesCommand()), new ExactTypeSpecification(typeof(Offer))));
}
private class FillModelPropertiesCommand : ISpecimenCommand
{
public void Execute(object specimen, ISpecimenContext context)
{
var offer = specimen as Offer;
offer.CompanyHistory = (CompanyHistory)context.Resolve(typeof(CompanyHistory));
}
}
}
This works, but it's far from perfect. As you can see, I refer to ModelSpecimenBuilder directly, so I'm dependent on implementation (as postprocessor I'd like not to be).
Answer posted by #Nikos is not satisfying, because his customization ignores previous customizations in chain of responsibility.
When we invoke the Create method, a CompositeSpecimenBuilder will invoke the Create method of all its contained builders until one of them provides a specimen. At this point the request is considered to be satisfied, and the rest of the builders are ignored.
source: AutoFixture Documentation
I'm trying to get Property Injection working on a Custom Action Filter Attribute. It is working as it is supposed to, however, I'd like to use DI on the Property itself. My filter looks like this
[AttributeUsage(AttributeTargets.Class)]
public sealed class HeaderFilterAttribute : ActionFilterAttribute
{
public IMarketService MarketService
{ get; set; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var view = (ViewResultBase)filterContext.Result;
if (view != null)
{
BaseViewModel viewModel = view.ViewData.Model as BaseViewModel;
if (viewModel != null)
viewModel.Header = GetHeaderScript();
}
base.OnActionExecuted(filterContext);
}
private string GetHeaderScript()
{
//Use MarketService here and return header script
return "script";
}
}
This is how I'm configuring the property using StructureMap inside my BootStrapper class.
//HeaderFilterAttribute
IMarketRepository marketRepository = new SqlMarketRepository();
IMarketService marketService = new MarketService(marketRepository);
ObjectFactory.Container.Configure(r => r.ForConcreteType<HeaderFilterAttribute>().
Configure.WithProperty("MarketService").
EqualTo(marketService));
My problem is I do not have access to SqlMarketRepository since all my concrete types are injected via DI and I really don't want to use concrete types in my bootstrapper. So the ultimate question now is, how do I inject MarketService into the Filter attribute without resorting to the above? :)
In your ObjectFactory.Initialize() call, add the following line:
SetAllProperties(x => x.OfType<IMarketService>());
That will inject the configured IMarketService instance into any property of type IMarketService, on any object retrieved from the container.
I think you need a custom action invoker implementation that will resolve the filters. You can dig a Windsor sample out of my company's implementation (about 1/2 way down). There should be several more available online. I know I've seen some on this site.
PS. I noticed you're using a base view model to populate a header. I'd recommend using the ViewData[] collection with a static key instead of inheritance in your view model. :)
I'm using a wrapper for the ASP.NET Membership provider so that I can have more loosely coupled usage of the library. I'm wanting to use StructureMap to provide true IoC, but I'm having trouble with configuring it with a User-to-Profile factory object that I'm using to put instantiate the profile in the context of a user. Here's the relevant details, first the interface and wrapper from the library:
// From ASP.Net MVC Membership Starter Kit
public interface IProfileService
{
object this[string propertyName] { get; set; }
void SetPropertyValue(string propertyName, object propertyValue);
object GetPropertyValue(string propertyName);
void Save();
}
public class AspNetProfileBaseWrapper : IProfileService
{
public AspNetProfileBaseWrapper(string email) {}
// ...
}
Next, a repository for interacting with a particular property in the profile data:
class UserDataRepository : IUserDataRepository
{
Func<MembershipUser, IProfileService> _profileServiceFactory;
// takes the factory as a ctor param
// to configure it to the context of the given user
public UserDataRepository(
Func<MembershipUser, IProfileService> profileServiceFactory)
{
_profileServiceFactory = profileServiceFactory;
}
public object GetUserData(MembershipUser user)
{
// profile is used in context of a user like so:
var profile = _profileServiceFactory(user);
return profile.GetPropertyValue("UserData");
}
}
Here's my first attempt at providing a StructureMap registry config, but it obviously doesn't work:
public class ProfileRegistry : Registry
{
public ProfileRegistry()
{
// doesn't work, still wants MembershipUser and a default ctor for AspNetProfileBaseWrapper
For<IProfileService>().Use<AspNetProfileBaseWrapper>();
}
}
Theoretically how I'd like to register it would look something like:
// syntax failure :)
For<Func<MembershipUser, IProfileService>>()
.Use<u => new AspNetProfileBaseWrapper(u.Email)>();
...where I can define the factory object in the configuration. This is obviously not valid syntax. Is there an easy way to accomplish this? Should I be using some other pattern to allow constructing my UserDataRepository in the context of a user? Thanks!
If I had looked at the overloads for Use, I would have found
Use(Func<IContext> func);
...which I can use as:
For<IUserService>().Use(s =>
new AspNetMembershipProviderWrapper(Membership.Provider));