Особенности использования вычислимых свойств во Flexberry ORM

Нехранимое свойство

Если свойство нехранимое (помечено атрибутом NotStored), оно не обрабатывается сервисами данных. Внутри аксессора get указывается какой-либо код (выражение), вычисляющий значение свойства.

Нехранимое свойство и представления

Разработчик должен внимательно следить, чтобы в представление, с которым происходит загрузка объектов сервисами данных, попадали все свойства, от которых зависит вычислимый атрибут.

Важно: Несоблюдение этого вызовет неверный счёт и ошибки.

Поведение сервиса данных при работе с вычислимым полем

Обычно сервисы данных при закачке списков не используют объекты данных, когда возвращают данные в виде строк. Это делается с целью повышения производительности, поскольку экономия вычислительных ресурсов на создании объектов данных очень высока. Однако, выражение для счёта вычислимого атрибута указано непосредственно в свойстве объекта данных, поэтому, когда объект данных содержит вычислимые атрибуты, сервисы данных поступают так: создаётся объект данных, свойства означиваются согласно представления, затем происходит конвертация в строку (поскольку сервис данных запросит значение вычислимого свойства, выполнится его счёт). Чтобы избежать счёта через объект данных и, соответственно, ускорить работу сервисов данных при закачке списков, применяется атрибут DataServiceExpression, которым для свойства назначается соответствие сервиса данных выражению, «понятному» этому сервису данных. Таким образом, если атрибут DataServiceExpression указан, то объект данных не создаётся, а счёт вычислимого атрибута «перекладывается» на сервис данных согласно переданному выражению.

Правила использования вычислимого поля

Принято, чтобы идентификаторы в выражении явно выделялись символом @ (собака).

Пример вычислимого свойства

[NotStored]
public Деньги Итого 
{ 
    get 
         { 
              return ИтогоДисконт + ИтогоВекселя - СуммаНДС;
         }
}

Пример вычислимого свойства с атрибутом DataServiceExpression

[DataServiceExpression(typeof(SQLDataService),"'\"'+ @НАИМЕНОВАНИЕ@+'\"'")]
[NotStored] 
public string НАИМЕНОВАНИЕВКАВЫЧКАХ 
{
    get
       {
           return "\""+ НАИМЕНОВАНИЕ+"\"";
       }
}

Пример использования детейлов

Пример использования детейлов при определении значения вычислимого поля доступен в статье Нехранимые (вычислимые) свойства объекта данных.

Первичные ключи в вычислимых полях

Использовать первичные ключи в вычислимых полях можно посредством указания STORMMainObjectKey без символа “@”.

Например, на списковой форме сообщение вида “Улица <Имя_Улицы> имеет первичный ключ <Первичный_Ключ_Улицы>".

[ICSSoft.STORMNET.NotStored())
[DataServiceExpression(typeof(ICSSoft.STORMNET.Business.SQLDataService), "\'Улица \' + @Название@ + \' имеет первичный ключ \' +CAST(STORMMainObjectKey as varchar(max))"))
public virtual string NotStoredName

При использовании данного свойства как мастерового в sql-запросе STORMMainObjectKey будет корректно заменён на STORMJoinedMasterKey.

('Улица ' + "IIS.TestStandWinforms.Дом"."Улица.Название" + ' имеет первичный ключ ' +CAST("STORMJoinedMasterKey0" as varchar(max))) as "Улица.NotStoredName"

Применение вычислимых полей в вычислимых полях

В связи в архитектурными особенностями генератора sql-запросов во Flexberry ORM, использование вычислимых полей в вычислимых полях на настоящий момент не поддерживается.

Например, есть класс Дом с мастером Улица.

У класса Улица есть вычислимое поле NotStoredName, вычисляемое следующим образом:

'Улица ' + @Название@ + ' имеет первичный ключ ' +CAST(STORMMainObjectKey as varchar(max))

Пусть есть потребность использовать данное поле при вычислении значения вычислимого поля класса Дом:

'Корпус ' + @Корпус@ + ';' + @Улица.NotStoredName@

Однако в таком варианте значение не будет подсчитано. Чтобы реализовать требуемую логику, можно расписать выражение, вычисляющее значение поля, следующим образом:

'Корпус ' + @Корпус@ + ';' + 'Улица ' + @Улица.Название@ + ' имеет первичный ключ ' +CAST(@Улица@ as varchar(max))

Пример создания нехранимого свойства данных

Процесс создания вычислимого свойства с помощью DataServiceExpression хорошо иллюстрирует следущая задача: Есть система учёта покупателей, где хранится информация об их покупках. У покупок может быть два статуса: “Передано в банк” и “Оплачено”. Необходимо определить у покупателя вычислимое поле “Сумма оплаченных покупок”.

В первую очередь следует создать во Flexberry Designer диаграмму классов.

Поле “СуммаОплаченныхПокупок” класса “Покупатель” сделать нехранимым, после чего в атрибут DataService Expression данного поля добавить строку:

DataService : ICSSoft.STORMNET.Business.SQLDataService;

DataService Expression:

"SELECT SUM(purchase."Сумма")
FROM "Покупатель" customer join "Покупка" purchase on customer."primaryKey" = purchase."Покупатель"
WHERE purchase."Покупатель" = StormMainObjectKey AND purchase."Статус" = 'Оплачено' "

Сгенерировать программный код.

Работа с программным кодом

Был сгенерирован следующий код:

[ICSSoft.STORMNET.NotStored()]
[DataServiceExpression(typeof(ICSSoft.STORMNET.Business.SQLDataService), "SELECT SUM(purchase.\"Сумма\")"+
" FROM \"Покупатель\" customer join \"Покупка\" purchase on customer.\"primaryKey\" = purchase.\"Покупатель\""+
" WHERE purchase.\"Покупатель\" = StormMainObjectKey AND  purchase.\"Статус\" = \'Оплачено\' ")]
public virtual decimal СуммаОплаченныхПокупок
{
	get {	return null;	}
	set {}
}

В результате при просмотре списков покупателей нехранимое поле “СуммаОплаченныхПокупок” вычисляется без создания объекта данных.

public class Покупатель : ICSSoft.STORMNET.DataObject
{
	private ICSSoft.STORMNET.UserDataTypes.NullableDecimal cashedPurchaseSum = null; //переменная для хранения кэша поля СуммаОплаченныхПокупок
	private ICSSoft.STORMNET.UserDataTypes.NullableDecimal cashedAvailableSum = null; //переменная для хранения кэша поля ДоступнаяСумма
	//...
}

Также нужно отредактировать код для СуммаОплаченныхПокупок.

[ICSSoft.STORMNET.NotStored()]
[DataServiceExpression(typeof(ICSSoft.STORMNET.Business.SQLDataService), "SELECT SUM(purchase.\"Сумма\")"+
" FROM \"Покупатель\" customer join \"Покупка\" purchase on customer.\"primaryKey\" = purchase.\"Покупатель\""+
" WHERE purchase.\"Покупатель\" = StormMainObjectKey AND  purchase.\"Статус\" = \'Оплачено\' ")]
public virtual decimal СуммаОплаченныхПокупок
{
	get
	{
		return this.cashedPurchaseSum;
	}
	set
	{
		if (value != null)
		{
			this.cashedPurchaseSum= value;
		}
	}}

Если расширить условие задачи, что в поле “Доступная сумма” класса “Покупатель” необходимо записать доступную сумму на счёте (то есть сумма на счёте минус сумма платежей, что имеет статус “Передано в банк”), то программный код для данного поля объекта может иметь следующий вид (почему this.СуммаНаСчёте в выражении записано как @СуммаНаСчёте@, было объяснено в статье Нехранимые (вычислимые) свойства объекта данных):

// *** Start programmer edit section *** (Покупатель.ДоступнаяСумма CustomAttributes)
[DataServiceExpression(typeof(SQLDataService), "SELECT @СуммаНаСчёте@ - SUM(purchase.\"Сумма\") "+
	" FROM \"Покупатель\" customer join \"Покупка\" purchase on customer.\"primaryKey\" = purchase.\"Покупатель\" "+
	" WHERE purchase.\"Покупатель\" = StormMainObjectKey AND  purchase.\"Статус\" = \'Передано в банк\' ")]
// *** End programmer edit section *** (Покупатель.ДоступнаяСумма CustomAttributes)
[ICSSoft.STORMNET.NotStored()]
public virtual decimal ДоступнаяСумма
{
	get
	{
		return this.cashedAvailableSum;
	}
	set
	{
		if (value != null)
		{
			this.cashedAvailableSum = value;
		}
	}
}