How to support dynamic multiinjection with Ninject, where there may be NO items bound - ninject

In the following unit tests, TestDirectRetrieval_WithNoImplementations and TestInjection_WithNoImplementations fail
[TestFixture]
public class KernelTests
{
[Test] //Passes
public void TestDirectRetrieval_WithMultipleImplementations()
{
//Arrange
var kernel = new StandardKernel();
kernel.Bind<Foo>().ToConstant(new Foo("a"));
kernel.Bind<Foo>().ToConstant(new Foo("b"));
//Act + Assert
Assert.DoesNotThrow(() => kernel.GetAll<Foo>().ToList());
}
[Test] //Passes
public void TestInjection_WithMultipleImplementations()
{
//Arrange
var kernel = new StandardKernel();
kernel.Bind<Foo>().ToConstant(new Foo("a"));
kernel.Bind<Foo>().ToConstant(new Foo("b"));
//Act + Assert
Assert.DoesNotThrow(() => kernel.Get<Bar>());
}
[Test] //Fails
public void TestDirectRetrieval_WithNoImplementations()
{
//Arrange
var kernel = new StandardKernel();
//Act + Assert
Assert.DoesNotThrow(() => kernel.GetAll<Foo>().ToList());
}
[Test] //Fails
public void TestInjection_WithNoImplementations()
{
//Arrange
var kernel = new StandardKernel();
//Act + Assert
Assert.DoesNotThrow(() => kernel.Get<Bar>());
}
#region Test helper classes
class Foo
{
public Foo(string someArgThatCantBeAutomaticallyResolvedByNinject){}
}
class Bar
{
private List<Foo> myFoos;
public Bar(IEnumerable<Foo> foos)
{
myFoos = foos.ToList();
}
}
#endregion
}
In a scenario were someone is binding things to constants / implementations based on some dynamic situation, how can they support the case were nothing is bound at all?

GetAll is not used by ninject for injection, but as you found out it provides the desired behavior. To enable injection with 0-n semantics I commonly roll my own "collection", an uncomplete / pseudo code version looks like this:
class OptionalItems<T> : IEnumerable<T>
{
private readonly T[] items;
public OptionalItems(IResolutionRoot resolutionRoot)
{
this.items = resolutionRoot.TryGetAll(T).ToArray();
}
public IEnumerator<T> GetEnumerator() {
return this.items.GetEnumerator();
}
}
Now inject OptionalItems instead of IEnumerable<T> or IList<T>.
For testability-reasons it might make sense to create an interface for OptionalItems: public interface IOperationalItems<T> : IEnumerable<T> and bind that.

Related

Inject all class that implement and interface ASP.NET Core

I have a base interface called IBaseService which I implement in many classes and I want to inject all classes that implement IBaseService.
Here is an example of my code:
public interface IBaseService
{
Task<bool> Create();
}
public class EstadoService : IBaseService
{
private readonly IUnitOfWork _unitOfWork;
private readonly IMapper _mapper;
private readonly IRepository<Estado> _repository;
public EstadoService()
{
}
public EstadoService(IUnitOfWork unitOfWork, IMapper mapper, IRepository<Estado> repository)
{
_unitOfWork = unitOfWork;
_mapper = mapper;
_repository = repository;
}
public async Task<bool> Create()
{
// Create Logic
}
}
public static object AddServices(this IServiceCollection services)
{
/*
typeof(IBaseService).Assembly.GetTypes()
.Where(mytype => mytype.GetInterface(typeof(IBaseService).Name) !=
null).ToList().ForEach(appCoreService =>
services.AddScoped(Activator.CreateInstance(appCoreService,)));
*/
var items = typeof(IBaseService).Assembly.GetTypes()
.Where(x => !x.IsAbstract && x.IsClass &&
x.GetInterface(nameof(IBaseService)) == typeof(IBaseService));
foreach (var item in items)
{
string _name = item.FullName;
Type t = Type.GetType(_name);
var instance = (IBaseService) Activator.CreateInstance(t);
services.Add(new ServiceDescriptor(typeof(IBaseService), instance, ServiceLifetime.Scoped));
}
return services;
}
public class EstadoController : BaseApiController
{
private readonly IEstadoService _estado;
public EstadoController(IEstadoService estado)
{
_estado = estado;
}
[HttpPost("add-estado")]
public async Task<ActionResult> AddEstado(EstadoRegisterDto registerEstadoDto)
{
await _estado.Create(registerEstadoDto);
return Ok();
}
}
It looks fine, but it doesn't work.
For the register operation, fix it a bit like this
// Register on Startup
var items = typeof(IBaseService).Assembly.GetTypes()
.Where(x => !x.IsAbstract && x.IsClass &&
x.GetInterface(nameof(IBaseService)) == typeof(IBaseService)).ToArray();
foreach (var item in items)
{
services.Add(new ServiceDescriptor(typeof(IBaseService), item, ServiceLifetime.Scoped));
}
// Using
public IActionResult TestInjection([FromServices] IEnumerable<IBaseService> estadoService)
{
// Place a debugger here to see the result
var allImplementedServices = estadoService.ToArray();
}
As I can see you just want to add services that inherit IBaseService interface to dependency injection.
In not dynamically scenario to add a service to dependency injection you have to do it like this:
services.AddScoped<IEstadoService, EstadoService>();
If you put it like this in your code this won't work because EstadoService does not implement IEstadoService and the class and interface are not related.
In your controller to access to the services your injecting IEstadoService so, to get this working the first you need to do is:
in IEstadoService inherit from IBaseService like this:
public interface IEstadoService: IBaseService
{
// code here...
}
and in EstadoService inherit from IEstadoService like this:
public class EstadoService: IEstadoService
{
// code here...
}
in this case you can add dependency injection dynamically like this:
public static object AddServices(this IServiceCollection services)
{
var list = Assembly.GetExecutingAssembly().GetTypes()
.Where(mytype => mytype.GetInterface(typeof(IBaseService).Name) != null && !mytype.IsInterface)
.ToList();
foreach (var item in list)
{
services.AddScoped(item.GetInterface($"I{item.Name}"), item);
}
return services;
}
The assembly list will have all services that inherit IBaseService in this case the list will have EstadoService.
As item in foreach loop is a Type we can call item.GetInterface($"I{item.Name}") which will result in interface object of type of IEstadoService
Like this it will work for all services that are implementing IBaseService

Can MS Fakes create future mock objects?

In TypeMock you can create a future mock object, for example:
public class ClassToTest
{
public ClassToTest()
{
var o = new Foo();
}
}
[Test]
public void Test()
{
var fakeFoo = Isolate.Fake.Instance<Foo>();
Isolate.Swap.NextInstance<Foo>().With(fakeFoo);
}
Does MS Fakes have the same functionality as the above?
I found a great example from this SO question which demonstrates how to fake future instances of objects. Here's an example from that question:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
ClassLibrary1.Child myChild = new ClassLibrary1.Child();
using (ShimsContext.Create())
{
ClassLibrary1.Fakes.ShimChild.AllInstances.addressGet = (instance) => "foo";
ClassLibrary1.Fakes.ShimParent.AllInstances.NameGet = (instance) => "bar";
Assert.AreEqual("foo", myChild.address);
Assert.AreEqual("bar", myChild.Name);
}
}
}
This looks like it will do the trick for me.

How to implement EF Code First and WCFDataService

A bit of history first. I created a EF Code First Library that contains POCO Objects as my Models, a generic DataProvider that inherits from DbContext, generic Repostory that implements the generic DataProvider, and a generic Service that implements the repository. I have used this library successfully in WPF (MVVM), ASP.Net, Window Forms, and ASP MVC applications.
For this discussion I will reference the Company Model
From the top down, I create a Service class called CompanyService that inherits from a base Service Class. The CompanyService class contains all of the business logic for the Company Model. This class uses the Repository class to perform the CRUD operations. The Repository then encapsulates all the DataProvider class operations.
I have done some research on using EF with WCFDataService, but I can't get my head around how to implement my library with it, particulary when it comes to overriding the CreateDataSource() Method.
It may be that I should just use a WCF Service instead, maybe I'm not understanding the purpose of the WCFDataService.
I have listed partial code for the classes involved:
public class CompanyService : ServiceBase<Company> ,ICompanyService
{
public Company GetCompanyByFolderId(string eFolderId)
{
return (Company)GetModelByFolderId(eFolderId);
}
}
public abstract class ServiceBase<TModel> : IService<TModel> where TModel : class, IModel
{
private IDataProvider _dataProvider;
public IDataProvider DataProvider
{
get
{
if (_dataProvider == null)
{
string connectionStringName = Properties.Settings.Default.DataProvider;
bool enableLazyLoading = true;
_dataProvider = new DataProvider(connectionStringName, enableLazyLoading);
}
return _dataProvider;
}
set
{
_dataProvider = value;
}
}
private IRepository<TModel> _repository;
public IRepository<TModel> Repository
{
get
{
if (_repository == null)
{
_repository = new Repository<TModel>(DataProvider);
}
return _repository;
}
set
{
_repository = value;
}
}
public TModel GetModelByFolderId(String folderId)
{
return GetTable().FirstOrDefault(o => o.EFolderid == folderId);
}
public virtual IQueryable<TModel> GetTable()
{
return Repository.GetTable();
}
}
public class Repository<TModel> : IRepository<TModel> where TModel : class, IModel
{
private IDataProvider _dataProvider;
public Repository(IDataProvider dataProvider)
{
_dataProvider = dataProvider;
}
private IDbSet<TModel> DbSet
{
get
{
return _dataProvider.Set<TModel>();
}
}
public IQueryable<TModel> GetTable()
{
return _dataProvider.GetTable<TModel>();
}
}
public class DataProvider : DbContext, IDataProvider
{
public DataProvider()
{
}
public DataProvider(string connectionStringName, bool enableLazyLoading = true)
: base(connectionStringName)
{
Configuration.LazyLoadingEnabled = enableLazyLoading;
//Configuration.ProxyCreationEnabled = false;
}
public new IDbSet<TModel> Set<TModel>() where TModel : class
{
return base.Set<TModel>();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new CompanyMapping());
base.OnModelCreating(modelBuilder);
}
public IQueryable<TModel> GetTable<TModel>() where TModel : class
{
return Set<TModel>().AsQueryable();
}
}
Then my Test looks something like this:
[TestClass()]
public class CompanyServiceTest
{
[TestMethod()]
public void GetCompanies()
{
CompanyService target = new CompanyService();
IQueryable<Company> companies = target.GetTable();
Assert.IsNotNull(companies);
}
[TestMethod()]
public void GetCompanyByFolderId()
{
CompanyService target = new CompanyService();
Company company = target.GetCompanyByFolderId("0000000000000000000000000172403");
Assert.IsNotNull(company);
}
}

Changing default object scope with Ninject 2.2

Is it possible to change the default object scope in Ninject 2.2? If so, how is it done?
As far as I can tell you could override AddBinding() on the BindingRoot (StandardKernel or NinjectModule) and modify the ScopeCallback property on the binding object.
public class CustomScopeKernel : StandardKernel
{
public CustomScopeKernel(params INinjectModule[] modules)
: base(modules)
{
}
public CustomScopeKernel(
INinjectSettings settings, params INinjectModule[] modules)
: base(settings, modules)
{
}
public override void AddBinding(IBinding binding)
{
// Set whatever scope you would like to have as the default.
binding.ScopeCallback = StandardScopeCallbacks.Singleton;
base.AddBinding(binding);
}
}
This test should now pass (using xUnit.net)
public class DefaultScopedService { }
[Fact]
public void Should_be_able_to_change_default_scope_by_overriding_add_binding()
{
var kernel = new CustomScopeKernel();
kernel.Bind<DefaultScopedService>().ToSelf();
var binding = kernel.GetBindings(typeof(DefaultScopedService)).First();
binding.ScopeCallback.ShouldBe(StandardScopeCallbacks.Singleton);
}
The CustomScopeKernel will also work with Ninject modules.
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<DefaultScopedService>().ToSelf();
}
}
[Fact]
public void Should_be_able_to_change_default_scope_for_modules()
{
var module = new ServiceModule();
var kernel = new CustomScopeKernel(module);
var binding = kernel.GetBindings(typeof(DefaultScopedService)).First();
binding.ScopeCallback.ShouldBe(StandardScopeCallbacks.Singleton);
}

Auto-mock container: Rhino Mocks and NInject

Does anyone have an implementation lying around of an auto-mock container using Rhino Mocks and NInject?
OK I built one myself using the Moq integration as a starting point. It is very simple. You need these 3 classes:
public class AutoMockingKernel : StandardKernel
{
private readonly IMockProvider _mockProvider;
public void Reset()
{
Components.Get<ICache>().Clear();
}
protected override bool HandleMissingBinding(Type service)
{
var isSelfBindable = TypeIsSelfBindable(service);
var binding = new Binding(service)
{
ProviderCallback = isSelfBindable
? StandardProvider.GetCreationCallback(service)
: _mockProvider.GetCreationCallback(),
IsImplicit = true
};
if (!isSelfBindable)
binding.ScopeCallback = ctx => null;
AddBinding(binding);
return true;
}
public AutoMockingKernel(IMockProvider mockProvider, INinjectSettings settings, params INinjectModule[] modules)
: base(settings, modules)
{
_mockProvider = mockProvider;
}
public AutoMockingKernel(IMockProvider mockProvider, params INinjectModule[] modules)
: base(modules)
{
_mockProvider = mockProvider;
}
}
internal class RhinoMockProvider : IProvider
{
public Type Type { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="RhinoMockProvider"/> class.
/// </summary>
public RhinoMockProvider(Type type)
{
Type = type;
}
public object Create(IContext context)
{
return MockRepository.GenerateMock(Type, Type.EmptyTypes);
}
}
public class RhinoAutoMockProvider : IMockProvider
{
public Func<IContext, IProvider> GetCreationCallback()
{
return ctx => new RhinoMockProvider(ctx.Request.Service);
}
}
You can then create an auto-mocking kernel in your unit test like this:
[Test]
public void Test()
{
var kernel = new AutoMockingKernel(new RhinoAutoMockProvider());
... etc
}
There is a RhinoMocks integration extension available.