Skip to content

Commit

Permalink
Возможность задать UpdateView + тесты
Browse files Browse the repository at this point in the history
  • Loading branch information
inaidanov authored and turbcool committed Jan 25, 2024
1 parent 1b12561 commit 2f5649d
Show file tree
Hide file tree
Showing 12 changed files with 1,741 additions and 1,200 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ private DataObject UpdateObject(EdmEntityObject edmEntity, object key)
{
// Создадим объект данных по пришедшей сущности.
// В переменной objs сформируем список всех объектов для обновления в нужном порядке: сам объект и зависимые всех уровней.
DataObject obj = GetDataObjectByEdmEntity(edmEntity, key, objs);
DataObject obj = GetDataObjectByEdmEntity(edmEntity, key, objs, useUpdateView: true);

for (int i = 0; i < objs.Count; i++)
{
Expand Down Expand Up @@ -698,12 +698,13 @@ private DataObject UpdateObject(EdmEntityObject edmEntity, object key)
}

/// <summary>
/// Получить объект данных по ключу: если объект есть в хранилище, то возвращается загруженным по представлению по умолчанию, иначе - создаётся новый.
/// Получить объект данных по ключу: если объект есть в хранилище, то возвращается загруженным по представлению <paramref name="view"/>, иначе - создаётся новый.
/// </summary>
/// <param name="objType">Тип объекта, не может быть <c>null</c>.</param>
/// <param name="keyValue">Значение ключа.</param>>
/// <param name="keyValue">Значение ключа.</param>
/// <param name="view">Представление для загрузки объекта.</param>
/// <returns>Объект данных.</returns>
private DataObject ReturnDataObject(Type objType, object keyValue)
private DataObject ReturnDataObject(Type objType, object keyValue, View view)
{
if (objType == null)
{
Expand All @@ -713,7 +714,6 @@ private DataObject ReturnDataObject(Type objType, object keyValue)
if (keyValue != null)
{
DataObject dataObjectFromCache = DataObjectCache.GetLivingDataObject(objType, keyValue);
View view = _model.GetDataObjectDefaultView(objType);

if (dataObjectFromCache != null)
{
Expand All @@ -727,16 +727,7 @@ private DataObject ReturnDataObject(Type objType, object keyValue)
IEnumerable<PropertyInView> ownProps = view.Properties.Where(p => !p.Name.Contains('.'));
if (!ownProps.All(p => loadedProps.Contains(p.Name)))
{
// Вычитывать объект сразу с детейлами нельзя, поскольку в этой же транзакции могут уже оказать отдельные операции с детейлами и перевычитка затрёт эти изменения.
View miniView = view.Clone();
DetailInView[] miniViewDetails = miniView.Details;
miniView.Details = new DetailInView[0];
_dataService.LoadObject(miniView, dataObjectFromCache, false, true, DataObjectCache);

if (miniViewDetails.Length > 0)
{
_dataService.SafeLoadDetails(view, new DataObject[] { dataObjectFromCache }, DataObjectCache);
}
_dataService.SafeLoadObject(dataObjectFromCache, view, DataObjectCache);
}
}

Expand Down Expand Up @@ -782,28 +773,49 @@ private DataObject ReturnDataObject(Type objType, object keyValue)
return obj;
}

/// <summary>
/// Добавляет объект данных в список на обновление если его там ещё нет.
/// </summary>
/// <param name="objsToUpdate">Список на обновление.</param>
/// <param name="dataObject">Объект данных который добавляем.</param>
/// <param name="insertToEnd">Добавлять в конец списка.</param>
private static void AddDataObject(List<DataObject> objsToUpdate, DataObject dataObject, bool insertToEnd)
{
bool objAlreadyExists = objsToUpdate.Any(o => PKHelper.EQDataObject(o, dataObject, false));
if (!objAlreadyExists)
{
if (insertToEnd)
{
objsToUpdate.Add(dataObject); // Добавляем в конец списка.
} else
{
objsToUpdate.Insert(0, dataObject); // Добавляем объект в начало списка.
}

}
}

/// <summary>
/// Построение объекта данных по сущности OData.
/// </summary>
/// <param name="edmEntity"> Сущность OData. </param>
/// <param name="key"> Значение ключевого поля сущности. </param>
/// <param name="dObjs"> Список объектов для обновления. </param>
/// <param name="endObject"> Признак, что объект добавляется в конец списка обновления. </param>
/// <param name="useUpdateView">Использовать представление для обновления (вместо представления по умолчанию).</param>
/// <returns> Объект данных. </returns>
private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object key, List<DataObject> dObjs, bool endObject = false)
private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object key, List<DataObject> dObjs, bool endObject = false, bool useUpdateView = false)
{
if (edmEntity == null)
{
return null;
}

IEdmEntityType entityType = (IEdmEntityType)edmEntity.ActualEdmType;
Type objType = _model.GetDataObjectType(_model.GetEdmEntitySet(entityType).Name);

// Значение свойства.
object value;

// Получим значение ключа.
IEdmEntityType entityType = (IEdmEntityType)edmEntity.ActualEdmType;
IEnumerable<IEdmProperty> entityProps = entityType.Properties().ToList();
var keyProperty = entityProps.FirstOrDefault(prop => prop.Name == _model.KeyPropertyName);
if (key != null)
Expand All @@ -815,26 +827,21 @@ private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object ke
edmEntity.TryGetPropertyValue(keyProperty.Name, out value);
}

// Загрузим объект из хранилища, если он там есть (используем представление по умолчанию), или создадим, если нет, но только для POST.
// Загрузим объект из хранилища, если он там есть, или создадим, если нет, но только для POST.
// Тем самым гарантируем загруженность свойств при необходимости обновления и установку нужного статуса.
DataObject obj = ReturnDataObject(objType, value);
Type objType = _model.GetDataObjectType(edmEntity);

// Добавляем объект в список для обновления, если там ещё нет объекта с таким ключом.
var objInList = dObjs.FirstOrDefault(o => PKHelper.EQDataObject(o, obj, false));
if (objInList == null)
View view = _model.GetDataObjectDefaultView(objType);
if (useUpdateView)
{
if (!endObject)
{
// Добавляем объект в начало списка.
dObjs.Insert(0, obj);
}
else
{
// Добавляем в конец списка.
dObjs.Add(obj);
}
view = _model.GetDataObjectUpdateView(objType) ?? view;
}

DataObject obj = ReturnDataObject(objType, value, view);

// Добавляем объект в список для обновления, если там ещё нет объекта с таким ключом.
AddDataObject(dObjs, obj, endObject);

// Все свойства объекта данных означим из пришедшей сущности, если они были там установлены(изменены).
string agregatorPropertyName = Information.GetAgregatePropertyName(objType);
IEnumerable<string> changedPropNames = edmEntity.GetChangedPropertyNames();
Expand Down Expand Up @@ -876,7 +883,7 @@ private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object ke
{
// Порядок вставки влияет на порядок отправки объектов в UpdateObjects это в свою очередь влияет на то, как срабатывают бизнес-серверы. Бизнес-сервер мастера должен сработать после, а агрегатора перед этим объектом.
bool insertIntoEnd = string.IsNullOrEmpty(agregatorPropertyName);
DataObject master = GetDataObjectByEdmEntity(edmMaster, null, dObjs, insertIntoEnd);
DataObject master = GetDataObjectByEdmEntity(edmMaster, null, dObjs, insertIntoEnd, useUpdateView);

Information.SetPropValueByName(obj, dataObjectPropName, master);

Expand Down Expand Up @@ -913,7 +920,8 @@ private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object ke
(EdmEntityObject)edmEnt,
null,
dObjs,
true);
true,
useUpdateView);

if (det.__PrimaryKey == null)
{
Expand Down Expand Up @@ -1008,20 +1016,7 @@ private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object ke

if (agregator != null)
{
DataObject existObject = dObjs.FirstOrDefault(o => PKHelper.EQDataObject(o, agregator, false));
if (existObject == null)
{
if (!endObject)
{
// Добавляем объект в начало списка.
dObjs.Insert(0, agregator);
}
else
{
// Добавляем в конец списка.
dObjs.Add(agregator);
}
}
AddDataObject(dObjs, agregator, endObject);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,37 @@ public static void SafeLoadDetails(this IDataService dataService, View view, ILi
}
}

/// <summary>
/// Догрузка объекта по указанному представлению, с загрузкой детейлов с сохранением состояния изменения.
/// </summary>
/// <param name="dataService">Экземпляр сервиса данных.</param>
/// <param name="dataObject">Объект данных, который нужно догрузить.</param>
/// <param name="view">Представление, которое используется для догрузки.</param>
/// <param name="dataObjectCache">Кеш объектов данных.</param>
public static void SafeLoadObject(this IDataService dataService, DataObject dataObject, View view, DataObjectCache dataObjectCache)
{
if (dataService == null)
{
throw new ArgumentNullException(nameof(dataService));
}

if (view == null)
{
throw new ArgumentNullException(nameof(view));
}

// Вычитывать объект сразу с детейлами нельзя, поскольку в этой же транзакции могут уже оказать отдельные операции с детейлами и перевычитка затрёт эти изменения.
View miniView = view.Clone();
DetailInView[] miniViewDetails = miniView.Details;
miniView.Details = new DetailInView[0];
dataService.LoadObject(miniView, dataObject, false, true, dataObjectCache);

if (miniViewDetails.Length > 0)
{
dataService.SafeLoadDetails(view, new DataObject[] { dataObject }, dataObjectCache);
}
}

/// <summary>
/// Add detail object to agregator according detail type.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,21 @@ public View GetDataObjectDefaultView(Type dataObjectType)
}

return _metadata[dataObjectType].DefaultView.Clone();
}

/// <summary>
/// Осуществляет получение представления для обновления объекта, соответствующего заданному типу объекта данных.
/// </summary>
/// <param name="dataObjectType">Тип объекта данных, для которого требуется получить представление для обновления.</param>
/// <returns>Представление для обновления объекта, соответствующее заданному типу объекта данных.</returns>
public View GetDataObjectUpdateView(Type dataObjectType)
{
if (dataObjectType == null)
{
throw new ArgumentNullException(nameof(dataObjectType), "Contract assertion not met: dataObjectType != null");
}

return _metadata[dataObjectType].UpdateView?.Clone();
}

/// <summary>
Expand All @@ -534,7 +549,7 @@ public List<Type> GetTypes(List<string> strTypes)
/// <summary>
/// Осуществляет получение типа объекта данных, соответствующего заданному имени набора сущностей в EDM-модели.
/// </summary>
/// <param name="edmEntitySetName">Имя набора сущностей в EDM-модели, для которого требуется получить представление по умолчанию.</param>
/// <param name="edmEntitySetName">Имя набора сущностей в EDM-модели, для которого требуется получить тип.</param>
/// <returns>Типа объекта данных, соответствующий заданному имени набора сущностей в EDM-модели.</returns>
public Type GetDataObjectType(string edmEntitySetName)
{
Expand All @@ -554,6 +569,22 @@ public Type GetDataObjectType(string edmEntitySetName)
return dataObjectType;
}

/// <summary>
/// Осуществляет получение типа объекта данных, соответствующего заданной сущности в EDM-модели.
/// </summary>
/// <param name="edmEntity">Сущность в EDM-модели, для которой требуется получить тип.</param>
/// <returns>Типа объекта данных, соответствующий заданной сущности в EDM-модели.</returns>
public Type GetDataObjectType(EdmEntityObject edmEntity)
{
if (edmEntity == null)
{
throw new ArgumentNullException(nameof(edmEntity));
}

IEdmEntityType entityType = (IEdmEntityType)edmEntity.ActualEdmType;
return GetDataObjectType(GetEdmEntitySet(entityType).Name);
}

/// <summary>
/// Получает список зарегистрированных в модели типов, которые являются дочерними к данному родительскому типу.
/// В список добавляется также сам родительский тип.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public sealed class DataObjectEdmTypeSettings
/// </summary>
public View DefaultView { get; set; }

/// <summary>
/// View to be used instead of DefaultView on updates (Patch/Batch).
/// </summary>
public View UpdateView { get; set; }

/// <summary>
/// The list of exposed details.
/// </summary>
Expand Down
Loading

0 comments on commit 2f5649d

Please sign in to comment.