Решение проблем использования типов данных в мастеровых ассоциациях с иерархией

При использовании наследования возникает проблема определения нужного типа при использовании ассоциации. Другими словами, если мастером типа является тип, связанный наследованием, то непонятно, какой конкретно из типов иерархии наследования является мастером.

Например, существует следующая модель:

Класс А имеет мастера М, от которого имеется, как минимум, два наследника: M1 и M2.

Соответственно, если имеется объект данных a (экземпляр класса A), то его мастером может быть экземпляр любого из классов M, M1, M2.

Сервисом данных сохранен объект a с мастером m1.

Проблема заключается в следующем: если теперь сервис данных читает объект а, как он «узнает», что мастер принадлежит классу M1 и, соответственно, находится в соответствующей ему структуре данных? Ведь ему известен только тип M, но принципиально никак невозможно узнать, какие типы унаследованы от M.

Атрибуты TypeUsage и PropertyTypeUsage

Для разрешения проблемы можно использовать специальные метаданные, позволяющие указать, что свойство M (ссылка на мастеровой класс) в классе данных A, в данном конкретном (сугубо прикладном) случае, может принимать не только значения типа M, а ещё и M1, и M2. В этом примере типы M1 и M2 называются используемыми типами.

Используемые типы указываются:

1.Атрибутом TypeUsage для мастерового/детейлового свойств класса данных, либо для класса-массива детейловых объектов (производного от DetailArray).

public class A:DataObject
{
	private M fM;
	
	[TypeUsage(new Type[]{typeof(M1), typeof(M2)})]
	public virtual M M {get{return fM;}set{fM=value;}}
}

public class M:DataObject
{
}

public class M1:M
{
}

public class M2:M
{
}

2.Атрибутом PropertyTypeUsage для класса данных, чьему свойству необходимо указать используемые типы. Атрибут аналогичен, однако к классу бывает писать нагляднее, чем к конкретному свойству, особенно, когда их много. Кроме того, такая запись используется при необходимости изменить TypeUsage в унаследованном классе, без перегрузки свойства.

Пример записи PropertyTypeUsage, эквивалентный TypeUsage:

[PropertyTypeUsage("M",new Type[]{typeof(M1), typeof(M2)})]
public class A:DataObject
{
	private M fM;		
	public virtual M M {get{return fM;}set{fM=value;}}
}

Метод Information.CheckUsingType позволяет проверить внутри свойства, метода, откуда вызван, совместим ли тип с объявленным, согласно используемых типов.

TypeUsageProvider.TypeUsage

Указанием дополнительных атрибутов невозможно полностью решить проблему используемых типов, поскольку может возникать ситуация вида:

Здесь класс-предок находится в отдельной сборке, поэтому TypeUsage прописать не удастся, так как в первой сборке необходимо знать типы M1 и M2 (находящиеся во второй сборке, ссылающейся на первую), а взаимные ссылки сборок друг на друга согласно .Net — невозможны. Дело может осложняться и тем, что разработчик, создающий сборку 2, в принципе не имеет доступа к исходному коду сборки 1 (соответственно, он не может объединить сборки в одну). Разумеется, TypeUsage прописывают и в этом случае, однако пользуются при этом статическим свойством статического класса TypeUsageProvider.TypeUsage. Здесь содержится разнообразная функциональность по управлению используемыми типами: можно прописывать используемые типы, узнавать/изменять их состав и т.п.

По мастеровым связям используемые типы можно указать для мастеров любой вложенности. Для этого необходимо указывать имена мастеровых свойств через точку. Методом TypeUsageProvider.TypeUsage.GetCombinedTypeUsage можно получить все типы в единой коллекции.