Я читал много документов о том, что такое DI и как его использовать (связанный с ядром ASP.NET). Как я понимаю, когда framework создает для меня некоторый контроллер, он каким-то образом знает, что класс этого контроллера должен передать конструктору. Это отражение или что-то еще? Может кто-нибудь показать мне, где я могу увидеть его в источниках ASP.NET Core GitHub?Как контейнер DI знает, что нужно конструкторам (Core ASP.NET)?
ответ
Поведение конструктора ASP.NET Core DI на текущем RC1 довольно сложно. В прошлом он поддерживал только типы с одним конструктором, который является very good default. В RC1, однако, он принимает типы с несколькими конструкторами. Тем не менее, его поведение очень странно, и во время тестирования я не смог позволить контейнеру DI создать компонент для меня, который имел несколько конструкторов.
Под крышками выбор конструктора и анализ параметров конструкторов выполняются с использованием отражения, и дерево выражений создается и в конечном итоге скомпилировано до делегата. Код так же просто, как this:
public Expression Build(Expression provider)
{
var parameters = _constructorInfo.GetParameters();
return Expression.New(
_constructorInfo,
_parameterCallSites.Select((callSite, index) =>
Expression.Convert(
callSite.Build(provider),
parameters[index].ParameterType)));
}
Вы можете начать смотреть here на GitHub.
В ореховой оболочке используется отражение для проверки общественных конструкторов типа и их параметров.
var constructors = _descriptor.ImplementationType.GetTypeInfo()
.DeclaredConstructors
.Where(constructor => constructor.IsPublic)
.ToArray();
Он сортирует конструкторы на основе длины параметра и затем выбирает лучший.
Этот фрагмент ищет лучший конструктор для вызова экземпляра типа.
public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
{
var constructors = _descriptor.ImplementationType.GetTypeInfo()
.DeclaredConstructors
.Where(constructor => constructor.IsPublic)
.ToArray();
IServiceCallSite[] parameterCallSites = null;
if (constructors.Length == 0)
{
throw new InvalidOperationException(Resources.FormatNoConstructorMatch(_descriptor.ImplementationType));
}
else if (constructors.Length == 1)
{
var constructor = constructors[0];
var parameters = constructor.GetParameters();
if (parameters.Length == 0)
{
return new CreateInstanceCallSite(_descriptor);
}
parameterCallSites = PopulateCallSites(
provider,
callSiteChain,
parameters,
throwIfCallSiteNotFound: true);
return new ConstructorCallSite(constructor, parameterCallSites);
}
Array.Sort(constructors,
(a, b) => b.GetParameters().Length.CompareTo(a.GetParameters().Length));
ConstructorInfo bestConstructor = null;
HashSet<Type> bestConstructorParameterTypes = null;
for (var i = 0; i < constructors.Length; i++)
{
var parameters = constructors[i].GetParameters();
var currentParameterCallSites = PopulateCallSites(
provider,
callSiteChain,
parameters,
throwIfCallSiteNotFound: false);
if (currentParameterCallSites != null)
{
if (bestConstructor == null)
{
bestConstructor = constructors[i];
parameterCallSites = currentParameterCallSites;
}
else
{
// Since we're visiting constructors in decreasing order of number of parameters,
// we'll only see ambiguities or supersets once we've seen a 'bestConstructor'.
if (bestConstructorParameterTypes == null)
{
bestConstructorParameterTypes = new HashSet<Type>(
bestConstructor.GetParameters().Select(p => p.ParameterType));
}
if (!bestConstructorParameterTypes.IsSupersetOf(parameters.Select(p => p.ParameterType)))
{
// Ambigious match exception
var message = string.Join(
Environment.NewLine,
Resources.FormatAmbigiousConstructorException(_descriptor.ImplementationType),
bestConstructor,
constructors[i]);
throw new InvalidOperationException(message);
}
}
}
}
if (bestConstructor == null)
{
throw new InvalidOperationException(
Resources.FormatUnableToActivateTypeException(_descriptor.ImplementationType));
}
else
{
Debug.Assert(parameterCallSites != null);
return parameterCallSites.Length == 0 ?
(IServiceCallSite)new CreateInstanceCallSite(_descriptor) :
new ConstructorCallSite(bestConstructor, parameterCallSites);
}
}
Спасибо, именно то, что я искал –
Он поддерживает типы с несколькими конструкторами. – Nkosi
@Nkosi: текущий rc1 does * not * принимает типы с несколькими конструкторами. – Steven
Я смотрю на неправильный код? Я мог бы не понимать код, который я читаю. https://github.com/aspnet/DependencyInjection/blob/4f2e6f035662b73936a2ed4fc249c163c9978c91/src/Microsoft.Extensions.DependencyInjection/ServiceLookup/Service.cs – Nkosi