I have an interface like this
public interface IPerson { }
And implementations
public class Fireman : IPerson
{
public string Name { get; set; }
public bool WithAssignedTruck { get; set; }
...
}
public class Pilot : IPerson
{
public string Name { get; set; }
public int Age { get; set; }
...
}
And pass them to a constructor
public class Registration : IRegistration
{
private readonly Fireman _fireman;
private readonly Pilot _pilot;
public Registration(Pilot pilot, Fireman fireman)
{
this._fireman = fireman;
this._pilot = pilot;
}
}
And here's what the initialization method looks like.
public T PopulateProfile<T>() where T : IPerson, new()
{
var personProfile = Activator.CreateInstance<T>();
...
return personProfile;
}
Please take note that this code is just an example.
I have a method that will set the value of each property of these classes which are from database. What I need to do is that, when I ask Ninject for any class that implements IPerson interface, Ninject should execute the method first, thus, Ninject will return an initialized class. Hope you could give me a hand. Thank you.
You can use Ninject.Extensions.Conventions in combination with an IBindingGenerator which generates a ToMethod binding:
BindingGenerator
internal class PersonBindingGenerator : IBindingGenerator
{
private static readonly MethodInfo PopulateOpenGenericMethodInfo =
typeof(IProfileService).GetMethod("PopulateProfile");
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(
Type type,
IBindingRoot bindingRoot)
{
yield return bindingRoot
.Bind(type)
.ToMethod(x => CreatePerson(x.Kernel.Get<IProfileService>(), type));
}
private static object CreatePerson(
IProfileService profileService,
Type type)
{
var closedGeneric = PopulateOpenGenericMethodInfo.MakeGenericMethod(type);
return closedGeneric.Invoke(profileService, new object[0]);
}
}
Bindings
kernel.Bind<IProfileService>().To<ProfileService>();
kernel.Bind(s => s
.FromThisAssembly()
.IncludingNonePublicTypes()
.SelectAllClasses()
.InheritedFrom<IPerson>()
.BindWith<PersonBindingGenerator>());
Test
Complete Test code for reference.
using FluentAssertions;
using Ninject;
using Ninject.Extensions.Conventions;
using Ninject.Extensions.Conventions.BindingGenerators;
using Ninject.Syntax;
using System;
using System.Collections.Generic;
using System.Reflection;
using Xunit;
namespace NinjectTest.SO36424126
{
public interface IPerson
{
string SomeValue { get; set; }
}
class BarPerson : IPerson
{
public string SomeValue { get; set; }
}
class FooPerson : IPerson
{
public string SomeValue { get; set; }
}
public interface IProfileService
{
T PopulateProfile<T>()
where T : IPerson, new();
}
internal class ProfileService : IProfileService
{
public T PopulateProfile<T>()
where T : IPerson, new()
{
var personProfile = Activator.CreateInstance<T>();
personProfile.SomeValue = "initialized";
return personProfile;
}
}
internal class PersonBindingGenerator : IBindingGenerator
{
private static readonly MethodInfo PopulateOpenGenericMethodInfo = typeof(IProfileService).GetMethod("PopulateProfile");
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
yield return bindingRoot
.Bind(type)
.ToMethod(x => CreatePerson(x.Kernel.Get<IProfileService>(), type));
}
private static object CreatePerson(IProfileService profileService, Type type)
{
var closedGeneric = PopulateOpenGenericMethodInfo.MakeGenericMethod(type);
return closedGeneric.Invoke(profileService, new object[0]);
}
}
public class Test
{
[Fact]
public void Foo()
{
var kernel = new StandardKernel();
kernel.Bind<IProfileService>().To<ProfileService>();
kernel.Bind(s => s
.FromThisAssembly()
.IncludingNonePublicTypes()
.SelectAllClasses()
.InheritedFrom<IPerson>()
.BindWith<PersonBindingGenerator>());
kernel.Get<BarPerson>().SomeValue.Should().Be("initialized");
}
}
}
Related
I have following code that does not compile
using System.Collections.Generic;
public interface IElement
{
}
public class AElement : IElement
{
public void DoSomethingSpecial()
{ }
}
public class Container<TElement>
{
public List<TElement> Elements { get; } = new();
}
public class Program
{
public static Container<IElement> GetContainer()
{
var concreteContainer = new Container<AElement>();
concreteContainer.Elements.ForEach(e => e.DoSomethingSpecial());
return concreteContainer; // Cannot implicitly convert type 'Container<AElement>' to 'Container<IElement>'
}
public static void Main()
{
var myContainer = GetContainer();
}
}
I read documentation about Covariance, Invariance, Contravariance and out Types.
And I am more confused than at the beginning.
Whats the way to fix this?
Code online: https://dotnetfiddle.net/85AgfT
You need to generate implicit conversion operator:
public class Container<IElement>
{
public List<IElement> Elements { get; } = new List<IElement>();
public static implicit operator Container<IElement>(Container<AElement> v)
{
//here you need to create Container<IElement> with your Container<AElement> 'v' values
return new Container<IElement>();
}
}
I finally got it working
using System.Collections.Generic;
public interface IContainer<out TElement>
{
}
public interface IElement
{
}
public class AElement : IElement
{
public void DoSomethingSpecial()
{ }
}
public class Container<TElement> : IContainer<TElement>
{
public List<TElement> Elements { get; } = new();
}
public class Program
{
public static IContainer<IElement> GetContainer()
{
var concreteContainer = new Container<AElement>();
concreteContainer.Elements.ForEach(e => e.DoSomethingSpecial());
return concreteContainer;
}
public static void Main()
{
var myContainer = GetContainer();
}
}
Make Container also an Interface and use an out Type parameter
I am trying to use the DataConnection class of linq2db in an abstract base class like so:
public abstract class BaseDbDao<T> : DataConnection where T : IDatabase
{
public string DBName => DBContext.Database;
public T DBContext { get; }
public DataConnection GetDataConnection()
{
return (this);
}
internal BaseDbDao(RelativityHelper helper, int workspaceId)
: base(GetDataProvider(), helper.GetDBContextNew(workspaceId).GetConnection())
{
DBContext = (T)helper.GetDBContextNew(workspaceId);
}
public static IDataProvider GetDataProvider()
{
LinqToDB.Common.Configuration.AvoidSpecificDataProviderAPI = true;
return new SqlServerDataProvider("", SqlServerVersion.v2017);
}
}
I am then calling it up from another class like so:
public class EdMeDBDao : BaseDbDao<MSSQLDBContext>
{
public ITable<EdMeTableRow> EdMeDatabase => GetTable<EdMeTableRow>();
public EdMeDBDao(RelativityHelper helper)
: base(helper, "123456")
{
}
public List<RelativityWorkspace> GetActiveProjects()
{
using (var db = GetDataConnection())
{
var a = GetTable<EdMeTableRow>().CaseName
};
}
}
However, I can't seem to call var a = GetTable<EdMeTableRow>().CaseName as ITable<EdMeTableRow> does not contain a definition for 'CaseName'
EdMeTableRow is defined as:
[Table(Name = "NuixWorkflowCases", Schema = "dbo")]
public class EdMeTableRow
{
public string CaseName { get; set; }
}
How can I access EdMeTableRow class from the EdMeDBDao class?
I would like to get all of the properties contained in a class whose types inherit from a certain abstract and generic class.
public abstract class foo<T> { }
public class fooInt_Indexed : foo<int> { }
public class fooInt_Not_Indexed : foo<int> { }
public class fooString_Compressed : foo<string> { }
public class fooString_Indexed : foo<string> { }
public class fooFloat : foo<float> { }
public abstract class bar
{
}
public class foobar : bar
{
public fooInt_Indexed value { get; set; }
public fooInt_Not_Indexed someOtherValue { get; set; }
public fooFloat someFloat { get; set; }
public otherData<int> {get; set; }
}
public class barChecker<T> where T : bar
{
public List<PropertyInfo> fooprops = new List<PropertyInfo>();
public static barChecker<T> Generator()
{
var #new = new barChecker<T>();
foreach (var item in typeof(T).GetProperties())
{
if (item.PropertyType is somesortof(foo<>)) #new.fooprops.Add(item);
}
return #new;
}
What do I need to put inside the barChecker<T> class code to make its fooprops list contain the property infos of "value","someOtherValue" and "someFloat" when generated as a barChecker<foobar> ?
Here's an extension method to System.Type that will answer this and similar questions about inheritance:
public static class TypeExtensions
{
public static bool InheritsFrom(this Type t, Type baseType)
{
if (t.BaseType == null)
{
return false;
}
else if (t == baseType)
{
return true;
}
else if (t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition().InheritsFrom(baseType))
{
return true;
}
else if (t.BaseType.InheritsFrom(baseType))
{
return true;
}
return false;
}
public static bool InheritsFrom<TBaseType>(this Type t)
=> t.InheritsFrom(typeof(TBaseType));
}
This here:
item.PropertyType is somesortof(foo<>)
Has to be replaced with
typeof(YourType).IsAssignableFrom(item.PropertyType)
The 'is' operator is only for real object instances, not if you already have a Type-Reference.
So in your case 'YourType' is typeof(barchecker< foobar >) ?
IQueryable extension method is fine in normal method, but not within a generic method.
Compile error: IQueryable does not contain a definition for MyExtension and the best extension method overload DataExtensions.MyExtension(IQueryable Fred) requires a receiver of type IQueryable Fred
Goal is turn the // do something interesting below into a generic method that would work acrross all FlintstoneObject types.
public static class RepetitiveCodeBelow
{
public static int RepetitiveCode()
{
var count = 0;
using (var context = new DataContext())
{
foreach (var data in context.Freds.AsNoTracking().Where(item => item.PrimaryKey > 0).MyExtension())
{
// do something interesting
}
foreach (var data in context.Barnies.AsNoTracking().Where(item => item.PrimaryKey > 0).MyExtension())
{
// do something interesting
}
// more types omitted
}
return count;
}
}
Working version:
public List<Fred> GetFredList()
{
using (var context = new DataContext())
{
return context.Freds.AsNoTracking().MyExtension().ToList();
}
}
Wont compile:
public List<T> GetList<T>() where T : FlintstoneObject<T>, new()
{
using (var context = new DataContext())
{
return context.Set<T>().AsNoTracking().MyExtension().ToList();
}
}
Full sample
public abstract class FlintstoneObject<T> where T : class
{
public abstract int PrimaryKey { get; set; }
}
public class Fred : FlintstoneObject<Fred>
{
public override int PrimaryKey { get; set; }
}
public class Barny : FlintstoneObject<Barny>
{
public override int PrimaryKey { get; set; }
}
public static class DataExtensions
{
public static IQueryable<Fred> MyExtension(this IQueryable<Fred> queryable)
{
return queryable;
}
}
public class DataContext : DbContext
{
public DbSet<Fred> Freds { get; set; }
public DbSet<Barny> Barnies { get; set; }
public List<Fred> GetFredList()
{
using (var context = new DataContext())
{
return context.Freds.AsNoTracking().MyExtension().ToList();
}
}
public List<T> GetList<T>() where T : FlintstoneObject<T>, new()
{
using (var context = new DataContext())
{
return context.Set<T>().AsNoTracking().MyExtension().ToList();
}
}
}
Your code is probably lacking of this extension:
public static IQueryable<T> MyExtension<T>(this IQueryable<T> queryable)
{
return queryable;
}
to make it compilable.
Anyway, what you intended to do with your generic stuff looks strange:
public class Fred : FlintstoneObject<Fred>
I thought T should be some class other than Fred which is inheriting the generic type FlintstoneObject<T>.
Is it possible that each class object has its own static data store?
I mean, just to perform actions like:
class Program
{
static void Main(string[] args)
{
var car1 = new Car();
car1.Save(); ////saves in its own storage
var own1 = new Owner();
own1.Save(); //saves in its own storage as well
}
}
In code I tried, I get such error
'System.InvalidCastException`
at this place
var repo = (Repository<IEntity>) CurrentRepository;
Whats wrong and how could I make it?
Whole code is here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
//var car1 = new Car();
//car1.Save(); ////saves in its own storage
//var own1 = new Owner();
//own1.Save(); //saves in its own storage as well var car1 = new Car();
}
}
public interface IEntity
{
long Id { get; }
}
public class Owner : Entity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Car Car { get; set; }
public Owner(string firstName, string lastName, Car car) : base(new Owner())
{
FirstName = firstName;
LastName = lastName;
Car = car;
}
public Owner() : base()
{
}
}
public class Car : Entity
{
public string Name { get; set; }
public int Year { get; set; }
public Car() : base()
{
}
public Car(string name, int year)
{
Name = name;
Year = year;
}
}
public abstract class Entity : IEntity
{
public long Id { get; }
public static object CurrentRepository { get; set; }
public Entity(Entity ent)
{
Type entityType = ent.GetType();
var instance = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(entityType));
CurrentRepository = instance;
}
public Entity()
{
}
public void Save()
{
var repo = (Repository<IEntity>)CurrentRepository;
repo.Save(this);
}
public void Delete()
{
var repo = (Repository<IEntity>)CurrentRepository;
repo.Delete(this);
}
}
public interface IRepository<T> where T : IEntity
{
void Save(T entity);
void Delete(T entity);
T Find(long id);
}
public class Repository<T> : IRepository<T> where T : class, IEntity
{
protected BaseStorage<T> CustomDataStorage;
public Repository()
{
CustomDataStorage = new BaseStorage<T>();
}
public void Save(T entity)
{
CustomDataStorage.Add(entity);
}
public void Delete(T entity)
{
CustomDataStorage.Remove(entity);
}
public T Find(long id)
{
throw new NotImplementedException();
}
}
public class BaseStorage<T> : IStorage<T>
{
List<T> data = new List<T>();
public void Add(T entity)
{
data.Add(entity);
}
public void Remove(T entity)
{
throw new NotImplementedException();
}
}
public interface IStorage<T>
{
void Add(T entity);
void Remove(T entity);
}
}
In code I tried, I get such error
'System.InvalidCastException`
at this place var repo = (Repository) CurrentRepository;
Whats wrong and how could I make it?
That's because you can't directly cast the CurrentRepository, a type of (Repository<Car> or Repository<Owner>) into a Repository<IEntity>.
See here for more information on this...
To achieve what you wanted here, you could try it this way:
Make the Entity class generic (Entity<T>) and constraint the generic type as IEntity
Make the CurrentRepository a type of Repository<Entity<T>>
Move the static initialization of CurrentRepository from instance constructor to the static constructor.
Make the Owner and Car object subclass of Entity<T> with T refers to its own type making it a self referencing generics
Code:
public class Owner : Entity<Owner>
{
...
}
public class Car : Entity<Car>
{
...
}
public abstract class Entity<T> : IEntity
where T: IEntity
{
public long Id { get; }
public static Repository<Entity<T>> CurrentRepository { get; set; }
static Entity()
{
CurrentRepository = new Repository<Entity<T>>();
}
public Entity()
{
}
public void Save()
{
CurrentRepository.Save(this);
}
public void Delete()
{
CurrentRepository.Delete(this);
}
}
One option to consider would be to change Entity as below.
The ConcurrentDictionary is to ensure that you get one repository per type.
public abstract class Entity : IEntity
{
public long Id { get; }
public static ConcurrentDictionary<Type, dynamic> CurrentRepository = new ConcurrentDictionary<Type, dynamic>();
public Entity(Entity ent)
{
GetRepository(ent);
}
private static dynamic GetRepository(Entity ent)
{
Type entityType = ent.GetType();
return CurrentRepository.GetOrAdd(entityType, type =>
{
var instance = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(entityType));
return instance;
});
}
public Entity()
{
}
public void Save()
{
var repo = GetRepository(this);
repo.Save((dynamic)this);
}
public void Delete()
{
var repo = GetRepository(this);
repo.Delete((dynamic)this);
}
}