Ninject summon graphs with argument - c#-4.0

Here is my problem. I have a presenter class, lets call it 'Presenter' that takes an IDataSource as a constructor argument. There are different implementations of the IDataSource interface. I would like to be able to pass some argument to Ninject and based on that argument one of several IDataSource implementations should by used. I've provided some sample code below. I think that my solution is really ugly and that there must be a smarter, cleaner way to do this. How are you guys solving this type of problem?
Here is my sample code
public class Presenter
{
public Presenter(IDataSource dataSource)
{
DataSource = dataSource;
}
private IDataSource DataSource { get; set; }
public List<string> GetData()
{
return DataSource.GetAll();
}
}
public class InMemoryDataSource : IDataSource
{
public List<string> GetAll()
{
return new List<string> {"a", "b"};
}
}
public class DbDataSource : IDataSource
{
public List<string> GetAll()
{
return new List<string> { "1", "2" };
}
}
public interface IDataSource
{
List<string> GetAll();
}
public class Module : NinjectModule
{
public override void Load()
{
Bind<Presenter>().To<Presenter>().Named("Db");
Bind<Presenter>().To<Presenter>().Named("InMemory");
Bind<IDataSource>().To<InMemoryDataSource> ().WhenParentNamed("InMemory");
Bind<IDataSource>().To<DbDataSource>().WhenParentNamed("Db");
}
}
[Test]
public void Run()
{
using (var kernel = new StandardKernel(new Module()))
{
var p = kernel.Get<Presenter>(x => x.Name == "InMemory");
foreach(var s in p.GetData())
{
Console.Out.WriteLine(s);
}
}
}

This depends on what you want to do. I assume that you want to use a different db for testing than for production. In this case would create the module with the production configuration in mind and simply Rebind everything for testing:
public class Presenter
{
public Presenter(IDataSource dataSource)
{
DataSource = dataSource;
}
private IDataSource DataSource { get; set; }
public List<string> GetData()
{
return DataSource.GetAll();
}
}
public class InMemoryDataSource : IDataSource
{
public List<string> GetAll()
{
return new List<string> {"a", "b"};
}
}
public class DbDataSource : IDataSource
{
public List<string> GetAll()
{
return new List<string> { "1", "2" };
}
}
public interface IDataSource
{
List<string> GetAll();
}
public class Module : NinjectModule
{
public override void Load()
{
Bind<Presenter>().To<Presenter>();
Bind<IDataSource>().To<DbDataSource>();
}
}
[Test]
public void Run()
{
using (var kernel = new StandardKernel(new Module()))
{
kernel.Rebind<IDataSource>().To<InMemoryDataSource>();
var p = kernel.Get<Presenter>();
foreach(var s in p.GetData())
{
Console.Out.WriteLine(s);
}
}
}

Related

Expression mapping using Automapper throws a System.EntryPointNotFoundException after migrating to Automapper 10

I am using automapper to map expressions between classes that implement IEnumerable. The base classes look like this:
public abstract class EntityDtoBase<T> : DtoBase<T> where T : EntityDtoBase<T>
{
public virtual int Id { get; set; }
}
public abstract class PersistenceDtoBase<T> : DtoBase<T> where T : PersistenceDtoBase<T>
{
public virtual int Id { get; set; }
}
public abstract class DtoBase<T> : IEnumerable<T> where T : DtoBase<T>
{
private readonly IList<T> _items;
public int Count => _items.Count;
protected DtoBase()
{
this._items = new List<T>();
}
public void Add(T item)
{
_items.Add(item);
}
/* other methods like AddRange... */
IEnumerator IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return _items.GetEnumerator();
}
}
After migrating to Automapper 10, expression mapping between classes enheriting from EntityDtoBase and PersistenceDtoBase throws a System.EntryPointNotFoundException : Entry point was not found. The configuration I am using in my project is similar to the one used in this unit test:
public class UserEntityDto : EntityDtoBase<UserEntityDto> { }
public class UserPersistenceDto : PersistenceDtoBase<UserPersistenceDto> { }
public class UserProfile : Profile
{
public UserProfile() { CreateMap<UserEntityDto, UserPersistenceDto>().ReverseMap(); }
}
public class UnitTest
{
private readonly IMapper _mapper;
public UnitTest()
{
var sp = CreateServices();
_mapper = sp.GetRequiredService<IMapper>();
}
private static IServiceProvider CreateServices()
{
return new ServiceCollection()
.AddAutoMapper(cfg =>
{
cfg.AddExpressionMapping();
cfg.AddCollectionMappers();
cfg.ForAllMaps((map, exp) => exp.MaxDepth(1));
cfg.AllowNullCollections = true;
cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
}, typeof(UnitTest).Assembly)
.BuildServiceProvider(false);
}
[Fact]
public void Should_Map_Expression()
{
Expression<Func<UserEntityDto, bool>> searchExpression = u => u.Id == 1;
var searchExpressionMapped = _mapper.Map<Expression<Func<UserPersistenceDto, bool>>>(searchExpression);
Assert.NotNull(searchExpressionMapped);
}
You can find the complete unit test project here. The test succeeds using Automapper 9 and fails using Automapper 10.

Console Application - DbContext instance cannot be used inside OnConfiguring

I'm using Asp.Net Core Console Application and Entiy Framework Core and Unit of Work repository pattern. When I'm using multi thread function, I get this error:
DbContext instance cannot be used inside OnConfiguring since it is still being configured at this point. This can happen if a second operation is started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
UnitOfwork.cs
public interface IUnitOfWork : IDisposable
{
void Commit();
ApplicationDbContext GetContext();
}
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _applicationDbContext;
public UnitOfWork(ApplicationDbContext applicationDbContext)
{
_applicationDbContext = applicationDbContext;
}
public void Commit()
{
try
{
_applicationDbContext.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public ApplicationDbContext GetContext()
{
return _applicationDbContext;
}
public void Dispose()
{
_applicationDbContext.Dispose();
}
}
IRepository.cs
public interface IGenericRepository<T>
where T : class, IEntity
{
List<T> GetAll(Expression<Func<T, bool>> filter = null,
Func<IQueryable<T>, IOrderedEnumerable<T>> orderBy = null,
string includeProperties = "");
T FindSingle(int id);
T FindBy(Expression<Func<T, bool>> predicate, string includeProperties = "");
void Add(T toAdd);
void Update(T toUpdate);
void Delete(int id);
void Delete(T entity);
}
Repository.cs
public class GenericRepository<T> : IGenericRepository<T>
where T : class, IEntity
{
private readonly IUnitOfWork _unitOfWork;
public GenericRepository(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public virtual List<T> GetAll(Expression<Func<T, bool>> filter = null,
Func<IQueryable<T>, IOrderedEnumerable<T>> orderBy = null,
string includeProperties = "")
{
IQueryable<T> query = _unitOfWork.GetContext().Set<T>();
if (filter != null)
{
query = query.Where(filter);
}
foreach (string includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
return query.ToList();
}
public virtual T FindSingle(int id)
{
return _unitOfWork.GetContext().Set<T>().Find(id);
}
public virtual T FindBy(Expression<Func<T, bool>> predicate, string includeProperties = "")
{
IQueryable<T> query = _unitOfWork.GetContext().Set<T>();
foreach (string includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
return query.Where(predicate).FirstOrDefault();
}
public virtual void Add(T toAdd)
{
_unitOfWork.GetContext().Set<T>().Add(toAdd);
}
public virtual void Update(T toUpdate)
{
_unitOfWork.GetContext().Entry(toUpdate).State = EntityState.Modified;
}
public virtual void Delete(int id)
{
T entity = FindSingle(id);
_unitOfWork.GetContext().Set<T>().Remove(entity);
}
public virtual void Delete(T entity)
{
_unitOfWork.GetContext().Set<T>().Remove(entity);
}
}
Business Services;
public interface IUserService
{
void CreateUser(UserEntity userEntity, bool commit = false);
}
public class UserService : IUserService
{
private readonly IGenericRepository<UserEntity> _userRepository;
private readonly IUnitOfWork _unitOfWork;
public UserService(IUnitOfWork unitOfWork, IGenericRepository<UserEntity> userRepository)
{
_unitOfWork = unitOfWork;
_userRepository = userRepository;
}
public void CreateUser(UserEntity userEntity, bool commit = false)
{
try
{
_userRepository.Add(userEntity);
if (commit)
_unitOfWork.Commit();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Console Main.cs;
class Program
{
public static ServiceProvider ServiceProvider;
static void Main(string[] args)
{
InitializeIoc();
Task.Run(() => { FuncA(); });
Task.Run(() => { FuncB(); });
Console.ReadLine();
}
private static void InitializeIoc()
{
ServiceProvider = new ServiceCollection()
.AddDbContext<ApplicationDbContext>()
.AddTransient<IUnitOfWork, UnitOfWork>()
.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>))
.AddTransient<IUserService, UserService>()
.BuildServiceProvider();
}
private static void FuncA()
{
var userService = ServiceProvider.GetService<IUserService>();
for (int i = 0; i < 100; i++)
{
userService.CreateUser(new UserEntity { FirstName = "FuncA_" + Guid.NewGuid(), LastName = "Last", CreatedDate = DateTime.Now }, false);
}
}
private static void FuncB()
{
var userService = ServiceProvider.GetService<IUserService>();
for (int i = 0; i < 100; i++)
{
userService.CreateUser(new UserEntity { FirstName = "FuncB_" + Guid.NewGuid(), LastName = "Last", CreatedDate = DateTime.Now }, false);
}
}
}
How can i solve this problem?
Thank you for your help.
The problem is that the used AddDbContext registers your ApplicationDbContext with ServiceLifetime.Scoped, but you are not creating scopes hence it effectively works as singleton, thus is shared and accessed concurrently by multiple threads which causes the exception in question (and potentially many other because DbContext is not thread safe).
The solution is to use scopes, e.g. call CreateScope and use returned object ServiceProvider property for resolving services:
private static void FuncA()
{
using (var scope = ServicePropvider.CreateScope())
{
var userService = scope.ServiceProvider.GetService<IUserService>();
// Do something ...
}
}
private static void FuncB()
{
using (var scope = ServicePropvider.CreateScope())
{
var userService = scope.ServiceProvider.GetService<IUserService>();
// Do something ...
}
}

AutoMapper property name conversions

I'm trying to register a mapping convention to handle mapping from classes with Pascal Case names to classes with underscore names with postfix and prefix, and back again. I've tried to follow examples, but cannot get my head around how it's supposed to work.
This is one of the many things I've tried, that looks like it should work (in my opinion :)), but it doesn't seem to do anything:
public class PascalCaseEntity
{
public string CallingSystem { get; set; }
}
public class UnderscoreWithPrefixAndPostfixEntity
{
public string p_calling_system_ { get; set; }
}
public class PartsMappings
{
public void Apply()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<FromUnderscoreMapping>();
cfg.AddProfile<ToUnderscoreMapping>();
cfg.CreateMap<PascalCaseEntity, UnderscoreWithPrefixAndPostfixEntity>()
.WithProfile("ToUnderscoreMapping");
cfg.CreateMap<UnderscoreWithPrefixAndPostfixEntity, PascalCaseEntity>()
.WithProfile("FromUnderscoreMapping");
});
}
}
public class FromUnderscoreMapping : Profile
{
protected override void Configure()
{
RecognizePrefixes("p_");
RecognizePostfixes("_");
SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
DestinationMemberNamingConvention = new PascalCaseNamingConvention();
}
public override string ProfileName
{
get { return "FromUnderscoreMapping"; }
}
}
public class ToUnderscoreMapping : Profile
{
protected override void Configure()
{
RecognizeDestinationPrefixes("p_");
RecognizeDestinationPostfixes("_");
SourceMemberNamingConvention = new PascalCaseNamingConvention();
DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention();
}
public override string ProfileName
{
get { return "ToUnderscoreMapping"; }
}
}
What am I missing here?
I finally found a working solution. I created two profiles, one for each "direction", and added the mappings to them.
I'm not too happy with it, since I'd rather have the mappings in the same file (grouping them on business area). But at least it works... :)
I also tried putting the registrations in the same Profile, and using the .WithProfile("ToUnderscoreWithPrefix") method, but I didn't get that to work.
Mapper.Initialize(cfg =>
{
cfg.AddProfile(new ToUnderscoreWithPrefixMappings());
cfg.AddProfile(new FromUnderscoreWithPrefixMappings());
});
public class ToUnderscoreWithPrefixMappings : Profile
{
protected override void Configure()
{
RecognizeDestinationPrefixes("P", "p");
SourceMemberNamingConvention = new PascalCaseNamingConvention();
DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention();
CreateMap<PascalCaseEntity, UnderscoreWithPrefixAndPostfixEntity>();
}
public override string ProfileName { get; } = "ToUnderscoreWithPrefix";
}
public class FromUnderscoreWithPrefixMappings : Profile
{
protected override void Configure()
{
RecognizePrefixes("P_", "p_");
RecognizePostfixes("_");
SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
DestinationMemberNamingConvention = new PascalCaseNamingConvention();
CreateMap<UnderscoreWithPrefixAndPostfixEntity, PascalCaseEntity>();
}
public override string ProfileName { get; } = "FromUnderscoreWithPrefix";
}

ServiceStack empty metadata

Seeing a strange problem, getting empty metata pages for xml,json and jvs.
Using the following command line app. How does one debug these issues?
namespace ConsoleApplication2
{
public struct NativeUser
{
public int login;
public string group;
public string name;
}
[DataContract]
public class User
{
private NativeUser _native;
public User() { }
public User(NativeUser native)
{
_native = native;
}
public static implicit operator NativeUser(User user)
{
return user._native;
}
public static implicit operator User(NativeUser native)
{
return new User(native);
}
// ReSharper disable InconsistentNaming
[DataMember]
public int login
{
get { return _native.login; }
set { _native.login = value; }
}
[DataMember]
public string group
{
get { return _native.group; }
set { _native.group = value; }
}
[DataMember]
public string name
{
get { return _native.name; }
set { _native.name = value; }
}
}
[Description("GET account, all or by list of groups or by list of logins")]
[Route("/accounts/{groups}", "GET")]
[Route("/accounts/{logins}", "GET")]
[Route("/accounts/", "GET")]
public class Accounts : IReturn<User[]>
{
public string[] groups { set; get; }
public int[] logins { set; get; }
public Accounts() { }
public Accounts(params int[] logins)
{
this.logins = logins;
}
public Accounts(params string[] groups)
{
this.groups = groups;
}
}
public class Host : AppHostHttpListenerBase
{
public Host() : base("Test",
typeof(Accounts).Assembly)
{
}
public override void Configure(Funq.Container container)
{
}
}
public class Servce : IService
{
public object Get(Accounts request)
{
return new List<User>(){new User(new NativeUser())};
}
}
class Program
{
static void Main(string[] args)
{
var host = new Host();
host.Init();
host.Start("http://+:12345/");
global::System.Console.ReadLine();
}
}
}
Nm, found the bug :
public class Accounts : IReturn<User[]>
needs to be
public class Accounts : IReturn<List<User>>
Another very note worthy thing: All DTO's and objects being passed back and fourth in the DTO's require an empty constructor in order for the metata data to be properly generated.
Not sure if this is by design or a bug

Ninject, passing constructor argument to the kernel

Here is my problem:
I want to pass in one of the values to the constructor every time I request an instance form the kernel. I written some code below to illustrate the problem. The test is not failing so I guess that this works, but it does look pretty ugly. Is there a better, cleaner way to accomplish this with Ninject? Or should I rethink my design? All suggestions are appreciated.
[TestFixture]
public class Sandbox
{
[Test]
public void Run_Forrest_Run()
{
using (var kernel = new StandardKernel(new Module()))
{
var connection = new Connection(Guid.NewGuid().ToString());
var downloader = kernel.Get<IDownloader>(new IParameter[] { new Parameter("connection", connection, false) });
Assert.That(downloader.Connection.Info, Is.EqualTo(connection.Info));
}
}
public class Downloader : IDownloader
{
public Downloader(Connection connection, ILogger logger)
{
Connection = connection;
Logger = logger;
}
public Connection Connection { get; private set; }
public void Download()
{
Logger.Log("Downloading...");
}
public ILogger Logger { get; private set; }
}
public interface IDownloader
{
Connection Connection { get; }
void Download();
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.Out.WriteLine(message);
}
}
public interface ILogger
{
void Log(string message);
}
public class Connection
{
public Connection(string info)
{
Info = info;
}
public string Info { get; private set; }
}
public class Module : NinjectModule
{
public override void Load()
{
Bind<ILogger>().To<ConsoleLogger>();
Bind<IDownloader>().To<Downloader>()
.WithConstructorArgument("connection", context =>
{
var p = context.Parameters.First(x => x.Name == "connection");
return p.GetValue(context, null);
});
}
}
}
If you always want to specify the Connection when resolving a IDownloader then I think the ConstructorArgument (which is a IParameter) is what you are looking for:
[Test]
public void Run_Forrest_Run()
{
using (var kernel = new StandardKernel(new Module()))
{
var connection = new Connection(Guid.NewGuid().ToString());
var downloader = kernel.Get<IDownloader>(new [] {
new ConstructorArgument("connection", connection) });
Assert.That(downloader.Connection.Info, Is.EqualTo(connection.Info));
}
}
public class Module : NinjectModule
{
public override void Load()
{
Bind<ILogger>().To<ConsoleLogger>();
Bind<IDownloader>().To<Downloader>();
}
}

Resources