Я использую Unity Register по механизму соглашения в следующем сценарии:
public interface IInterface { }
public class Implementation : IInterface { }
Учитывая класс Implementation
и его интерфейс, я запускаю RegisterTypes
следующим образом:
unityContainer.RegisterTypes(
new[] { typeof(Implementation) },
WithMappings.FromAllInterfaces,
WithName.Default,
WithLifetime.ContainerControlled);
После этого вызова unitContainer
содержит три регистрации:
-
IUnityContainer
→IUnityContainer
(ok) -
IInterface
→Implementation
(ok) -
Implementation
→Implementation
(???)
Когда я меняю вызов следующим образом:
unityContainer.RegisterTypes(
new[] { typeof(Implementation) },
WithMappings.FromAllInterfaces,
WithName.Default);
Контейнер содержит только две регистрации:
-
IUnityContainer
→IUnityContainer
(ok) -
IInterface
→Implementation
(ok)
(это желаемое поведение).
После просмотра в исходного кода Unity я заметил, что есть некоторые недоразумения о том, как IUnityContainer.RegisterType
должен работать.
Метод RegisterTypes
работает следующим образом (комментарии указывают, каковы значения в приведенных выше сценариях):
foreach (var type in types)
{
var fromTypes = getFromTypes(type); // { IInterface }
var name = getName(type); // null
var lifetimeManager = getLifetimeManager(type); // null or ContainerControlled
var injectionMembers = getInjectionMembers(type).ToArray(); // null
RegisterTypeMappings(container, overwriteExistingMappings, type, name, fromTypes, mappings);
if (lifetimeManager != null || injectionMembers.Length > 0)
{
container.RegisterType(type, name, lifetimeManager, injectionMembers); // !
}
}
Поскольку fromTypes
не пуст, RegisterTypeMappings
добавляет одно типа: IInterface
→ Implementation
(правильно).
Тогда, если lifetimeManager
не является нулевым, код пытается изменить диспетчер времени жизни следующим вызовом:
container.RegisterType(type, name, lifetimeManager, injectionMembers);
Это имя функции полностью вводит в заблуждение, потому что документация четко заявляет, что:
RegisterType a LifetimeManager для данного типа и имени в контейнере. Для этого типа не выполняется сопоставление типов.
К сожалению, не только имя вводит в заблуждение, но и документация неверна. При отладке этого кода я заметил, что когда в сценариях, представленных выше, нет отображения из type
(Implementation
), он добавляется (как type
→ type
) и почему мы заканчиваем с тремя регистрациями в первом сценарии.
Я загрузил источники Unity, чтобы исправить эту проблему, но я нашел следующее unit test:
[TestMethod]
public void RegistersMappingAndImplementationTypeWithLifetimeAndMixedInjectionMembers()
{
var container = new UnityContainer();
container.RegisterTypes(new[] { typeof(MockLogger) }, getName: t => "name", getFromTypes: t => t.GetTypeInfo().ImplementedInterfaces, getLifetimeManager: t => new ContainerControlledLifetimeManager());
var registrations = container.Registrations.Where(r => r.MappedToType == typeof(MockLogger)).ToArray();
Assert.AreEqual(2, registrations.Length);
// ...
- это почти точно мой случай и приводит к моему вопросу:
Почему это ожидалось? Является концептуальной ошибкой, созданной unit test для соответствия существующему поведению, но не обязательно правильной, или мне не хватает чего-то важного?
Я использую Unity v4.0.30319.