【发布时间】:2016-02-18 11:32:55
【问题描述】:
我正在使用 Unity,并希望解析所有具有可从特定类型分配的泛型类型的实现。
我目前是这样注册的:
container.RegisterType<IQueryGenerator<IPerson>, PersonQueryGenerator>("1");
container.RegisterType<IQueryGenerator<ICustomer>, CustomerQueryGenerator>("2");
container.RegisterType<IQueryGenerator<IEmployee>, EmployeeQueryGenerator>("3");
container.RegisterType<IQueryGenerator<IManager>, ManagerQueryGenerator>("4");
然后层次结构是这样的:
public interface IPerson
{
}
public interface ICustomer : IPerson
{
}
public interface IEmployee : IPerson
{
}
public interface IManager : IEmployee
{
}
以及我正在尝试解决的实现的接口:
public interface IQueryGenerator<T>
{
IEnumerable<Expression<Func<T, bool>>> GenerateQueries();
}
现在我希望能够解析所有 IQueryGenerators,其中泛型类型可分配给我拥有的类型。所以我有一个应该包装它的工厂:
public class QueryFactory
{
private readonly IUnityContainer _container;
public QueryFactory(IUnityContainer container)
{
_container = container;
}
public IEnumerable<Expression<Func<TModel, bool>>> GenerateQueries<TModel>()
{
// Get all querygenerators which can handle TModel.
IQueryGenerator<TModel>[] queryGenerators = _container.ResolveAll<IQueryGenerator<TModel>>();
foreach (var queryGenerator in queryGenerators)
{
// Do something with queryGenerator.
var result = queryGenerator.GenerateQueries();
}
}
}
我想要的是_container.ResolveAll<IQueryGenerator<IEmployee>>() 返回PersonQueryGenerator、EmployeeQueryGenerator 和ManagerQueryGenerator,因为IManager 是IEmployee 而IEmployee 是IPerson。
_container.ResolveAll<IQueryGenerator<ICustomer>>() 返回PersonQueryGenerator 和CustomerQueryGenerator。
IEmployee可以向下转换为IPerson,可以由PersonQueryGenerator处理。
如何注册 IQueryGenerators,以及如何根据TModel 解决它们?或者有没有其他方法可以根据接口TModel实现来检索QueryGenerators?
编辑:
我添加了我想要做的概念验证。它运行良好,但非常硬编码。
class Program
{
static void Main(string[] args)
{
IEnumerable<IManager> managers = new List<IManager>
{
new Manager()
{
Birthday = DateTime.Now.AddDays(1),
Name = "Executive Manager",
EmployeeNumber = 9,
IsExecutive = true,
Salary = 1000
},
new Manager()
{
Birthday = DateTime.Now.AddDays(-1),
Name = "Ordinary Manager",
EmployeeNumber = 8,
IsExecutive = false,
Salary = 900
},
};
var queries = GenerateQueries();
managers = queries.Aggregate(managers, (current, expression) => current.Where(expression));
foreach (var manager in managers)
{
Console.WriteLine("Manager: {0}", manager.Name);
}
Console.ReadKey();
}
// Change this to a generic method.
static IEnumerable<Func<IManager, bool>> GenerateQueries()
{
// Change this to a more generic management
IList<IQueryGenerator<IManager>> queryGenerators = new List<IQueryGenerator<IManager>>();
if (typeof(IPerson).IsAssignableFrom(typeof(IManager)))
{
queryGenerators.Add(new PersonQueryGenerator());
}
if (typeof(IEmployee).IsAssignableFrom(typeof(IManager)))
{
queryGenerators.Add(new EmployeeGenerator());
}
if (typeof(IManager).IsAssignableFrom(typeof(IManager)))
{
queryGenerators.Add(new ManagerQueryGenerator());
}
// Fetch queries and return them.
var queries = new List<Func<IManager, bool>>();
foreach (var queryGenerator in queryGenerators)
{
// Do something with queryGenerator.
queries.AddRange(queryGenerator.GenerateQueries());
}
return queries;
}
// I want to use this method instead.
static IEnumerable<Func<TModel, bool>> GenerateQueries<TModel>()
{
// Do the same thing as the method above
//TODO: Resolve all IQueryGenerators that can handle TModel.
return null;
}
}
public interface IQueryGenerator<in T>
{
IEnumerable<Func<T, bool>> GenerateQueries();
}
public class ManagerQueryGenerator : IQueryGenerator<IManager>
{
public IEnumerable<Func<IManager, bool>> GenerateQueries()
{
Console.WriteLine("ManagerQueryGenerator called");
yield return x => x.IsExecutive;
}
}
public class PersonQueryGenerator : IQueryGenerator<IPerson>
{
public IEnumerable<Func<IPerson, bool>> GenerateQueries()
{
Console.WriteLine("PersonQueryGenerator called");
yield return x => x.Birthday > DateTime.Now;
}
}
public class EmployeeGenerator : IQueryGenerator<IEmployee>
{
public IEnumerable<Func<IEmployee, bool>> GenerateQueries()
{
Console.WriteLine("EmployeeGenerator called");
yield return x => x.Salary > 900;
}
}
public interface IHaveName
{
string Name { get; set; }
}
public interface IPerson : IHaveName
{
DateTime Birthday { get; set; }
}
public interface ICustomer : IPerson
{
int MoneyToSpend { get; set; }
}
public interface IEmployee : IPerson
{
int EmployeeNumber { get; set; }
int Salary { get; set; }
}
public interface IManager : IEmployee
{
bool IsExecutive { get; set; }
}
public class Manager : IManager
{
public string Name { get; set; }
public DateTime Birthday { get; set; }
public int EmployeeNumber { get; set; }
public int Salary { get; set; }
public bool IsExecutive { get; set; }
}
输出是:
PersonQueryGenerator called
EmployeeGenerator called
ManagerQueryGenerator called
Manager: Executive Manager
这正是我想要的,但具有更通用的类型管理,我真的很想使用Expression<Func<T,bool>>,因为它将与 EF 一起使用。
public interface IQueryGenerator<T>
{
IEnumerable<Expression<Func<T, bool>>> GenerateQueries();
}
代替:
public interface IQueryGenerator<in T>
{
IEnumerable<Func<T, bool>> GenerateQueries();
}
【问题讨论】:
-
但是
queryGenerators的类型是什么?object[]? -
但是
IQueryGenerator<IPerson>可以从IQueryGenerator<IManager>分配吗?我不这么认为。 -
@YacoubMassad - 不,你完全正确。它们不能相互分配。但是,IPerson 可以从 IManager 分配,我希望 Unity 能够理解这一点。但也许不可能?
-
我认为这行不通,因为类型不兼容。你将如何使用
GenerateQueries的返回值?result的类型是什么?你会如何消费它? -
@YacoubMassad - 我已经用运行的概念证明更新了这个问题。但是,它不使用通用。但它表明您可以为同一模型使用不同的 IQueryGenerator,只要该模型可分配给某个接口。
标签: c# generics unity-container