Создание расширений для изменения типов карточек ICardMetadataExtension
Создание расширений для изменения типов карточек ICardMetadataExtension¶
Расширения ICardMetadataExtension позволяют изменять типы карточек и информацию по секциям и колонкам при построении метаинформации. Построение происходит на сервере при первичном запуске application pool или при обращении к метаинформации после её сброса, например, после того, как пользователь изменил типы карточек или схему данных.
Расширение реализует интерфейс ICardMetadataExtension, или наследуется от CardMetadataExtension, переопределяя только нужные методы:
-
ModifyTypes - изменяет типы карточек, по которым затем будет строиться метаинформация. Принимает контекст расширения, содержащий изменяемую коллекцию типов CardTypes. Любые действия, связанные с изменением типов карточек, но не изменяющие секции и колонки, рекомендуется выполнять именно в этом методе.
-
ModifyMetadata - изменяет метаинформацию по типам карточек после её построения. К этому моменту уже определена коллекция доступных типов карточек, т.е. она получена из базы данных и изменена вызовом ModifyTypes. Секции, колонки и перечисления были созданы стандартными средствами платформы, но в этом методе их можно изменить. Например, добавить новые виртуальные секции с произвольной структурой, которые затем присоединить к существующим типам карточек.
Расширение должно быть зарегистрировано как на сервере (для фактического использования), так и на клиенте для специальных сценариев построения метаинформации (таких, как предпросмотр типа карточки с расширениями в TessaAdmin).
В качестве примера использования расширения рассмотрим задачу динамического изменения типов карточек, для которых требуется использовать процесс согласования KrProcess. Процесс со стороны физической структуры данных целиком и полностью реализован в карточке-сателлите, которая добавляется к нужным типам карточек в расширениях. То, какие именно типы могут использоваться в процессе согласования, определяется в карточке настроек KrSettings. В структуру согласуемых карточек требуется добавить виртуальные секции, вкладку “Процесс согласования” и валидаторы, которые добавлены в специальный скрытый тип карточки KrCard.
Расширение должно во все типы карточек, включённые в карточку настроек процесса согласования, добавить секции, вкладки и валидаторы из типа KrCard. При работе на сервере получение списка типов карточек выполняется из базы данных (метод CheckTypeInServerMode), а на клиенте в случае предпросмотра - из кэша карточек ICardCache, откуда может быть получена карточка настроек со списком типов. Расширение объявляется в Shared-сборке, но по-разному регистрируется на клиенте и на сервере.
public sealed class KrCardMetadataExtension : CardMetadataExtension
{
public KrCardMetadataExtension(ICardMetadata clientCardMetadata, ICardCache clientCache)
{
if (clientCardMetadata == null)
{
throw new ArgumentNullException("clientCardMetadata");
}
if (clientCache == null)
{
throw new ArgumentNullException("clientCache");
}
this.clientMode = true;
this.clientCardMetadata = clientCardMetadata;
this.clientCache = clientCache;
}
public KrCardMetadataExtension(IDbScope serverDbScope)
{
if (serverDbScope == null)
{
throw new ArgumentNullException("serverDbScope");
}
this.clientMode = false;
this.serverDbScope = serverDbScope;
}
private readonly bool clientMode;
private readonly ICardMetadata clientCardMetadata;
private readonly ICardCache clientCache;
private readonly IDbScope serverDbScope;
private delegate bool CheckTypeFunc(CardType cardType, ref List<Guid> allowedCardTypeIDs);
private bool CheckTypeInClientMode(CardType cardType, ref List<Guid> allowedCardTypeIDs)
{
if (cardType.InstanceType != CardInstanceType.Card)
{
return false;
}
if (allowedCardTypeIDs == null)
{
if (!this.clientCache.Cards.IsAllowed("KrSettings"))
{
return false;
}
Card krSettings = this.clientCache.Cards["KrSettings"];
allowedCardTypeIDs =
krSettings
.Sections["KrSettingsCardTypes"]
.Rows
.Where(x => x.State == CardRowState.None)
.Select(x => x.Get<Guid>("CardTypeID"))
.Distinct()
.ToList();
allowedCardTypeIDs.Remove(DefaultCardTypes.KrCardTypeID);
}
return allowedCardTypeIDs.Contains(cardType.ID);
}
private bool CheckTypeInServerMode(CardType cardType, ref List<Guid> allowedCardTypeIDs)
{
if (cardType.InstanceType != CardInstanceType.Card)
{
return false;
}
if (allowedCardTypeIDs == null)
{
using (this.serverDbScope.Create())
{
allowedCardTypeIDs = this.serverDbScope.Db
.SetCommand("select CardTypeID from KrSettingsCardTypes with(nolock)")
.LogCommand()
.ExecuteScalarList<Guid>();
allowedCardTypeIDs.Remove(DefaultCardTypes.KrCardTypeID);
}
}
return allowedCardTypeIDs.Contains(cardType.ID);
}
public override void ModifyTypes(ICardMetadataExtensionContext context)
{
CardType krCardType;
if (!context.CardTypes.TryGetValue(DefaultCardTypes.KrCardTypeID, out krCardType))
{
// если в метаинформации, которая строится, есть тип карточки с процессом согласования и отсутствует тип карточки KrCard, то:
// - на клиенте можем получить тип из серверной метаинформации;
// - на сервере расширение в таком случае не выполняет действий, т.к. на сервере метаинформация строится сразу по всем типам и нужного типа просто нет.
if (!this.clientMode
|| !this.clientCardMetadata.CardTypes.TryGetValue(DefaultCardTypes.KrCardTypeID, out krCardType))
{
return;
}
}
CheckTypeFunc checkTypeFunc = this.clientMode
? (CheckTypeFunc)CheckTypeInClientMode
: CheckTypeInServerMode;
List<Guid> allowedCardTypeIDs = null;
foreach (CardType targetCardType
in context
.CardTypes
.Where(x => checkTypeFunc(x, ref allowedCardTypeIDs)))
{
// быстрее скопировать весь тип карточки, чем отдельные его части
CardType krCardTypeClone = krCardType.DeepClone();
targetCardType.SchemeItems.AddRange(krCardTypeClone.SchemeItems);
targetCardType.Forms.AddRange(krCardTypeClone.Forms);
targetCardType.Validators.AddRange(krCardTypeClone.Validators);
}
}
}
Регистрация расширения на сервере:
extensionContainer
.RegisterExtension<ICardMetadataExtension, KrCardMetadataExtension>(x => x
.WithOrder(ExtensionStage.AfterPlatform)
.WithUnity(unityContainer
.RegisterType<KrCardMetadataExtension>(
new ContainerControlledLifetimeManager(),
new InjectionConstructor(typeof(IDbScope)))));
Регистрация расширения на клиенте (для предпросмотра):
extensionContainer
.RegisterExtension<ICardMetadataExtension, KrCardMetadataExtension>(x => x
.WithOrder(ExtensionStage.AfterPlatform)
.WithUnity(unityContainer
.RegisterType<KrCardMetadataExtension>(
new ContainerControlledLifetimeManager(),
new InjectionConstructor(
typeof(ICardMetadata),
typeof(ICardCache)))));