Skip to content

Commit

Permalink
Возможность задать UpdateView + тесты
Browse files Browse the repository at this point in the history
  • Loading branch information
turbcool committed Mar 20, 2024
1 parent c356535 commit 3197b6a
Show file tree
Hide file tree
Showing 8 changed files with 302 additions and 35 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Added
1. `updateViews` parameter of `DefaultDataObjectEdmModelBuilder` class. It allows to change update views for data objects (update view is used for loading a data object during OData update requests).
2. `masterLightLoadTypes` and `masterLightLoadAllTypes` parameters of `DefaultDataObjectEdmModelBuilder` class. They allow to change loading mode of masters during OData update requests.

### Changed
1. Updated Flexberry ORM up to 7.2.0-beta01.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace NewPlatform.Flexberry.ORM.ODataService.Controllers
namespace NewPlatform.Flexberry.ORM.ODataService.Controllers
{
using System;
using System.Collections.Generic;
Expand All @@ -23,9 +23,11 @@

#if NETFRAMEWORK
using System.Net.Http.Formatting;
using System.Web;
using System.Web.Http;
using System.Web.Http.Results;
using System.Web.Http.Validation;
using Newtonsoft.Json.Linq;
using NewPlatform.Flexberry.ORM.ODataService.Events;
using NewPlatform.Flexberry.ORM.ODataService.Handlers;
#endif
Expand All @@ -38,10 +40,10 @@
using NewPlatform.Flexberry.ORM.ODataService.Middleware;
#endif

/// <summary>
/// Определяет класс контроллера OData, который поддерживает запись и чтение данных с использованием OData формата.
/// </summary>
public partial class DataObjectController
/// <summary>
/// Определяет класс контроллера OData, который поддерживает запись и чтение данных с использованием OData формата.
/// </summary>
public partial class DataObjectController
{
/// <summary>
/// Метаданные файлов, временно загруженных в каталог файлового хранилища и привязанных к свойствам обрабатываемых объектов данных.
Expand Down Expand Up @@ -774,7 +776,7 @@ private DataObject ReturnDataObject(Type objType, object keyValue, View view)
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];
Expand All @@ -790,7 +792,7 @@ private DataObject ReturnDataObject(Type objType, object keyValue, View view)
return dataObjectFromCache;
}

// Вычитывать объект сразу с детейлами нельзя, поскольку в этой же транзакции могут уже оказать отдельные операции с детейлами и перевычитка затрёт эти изменения.
// Вычитывать объект сразу с детейлами нельзя, поскольку в этой же транзакции могут уже оказаться отдельные операции с детейлами и перевычитка затрёт эти изменения.
View lightView = view.Clone();
DetailInView[] lightViewDetails = lightView.Details;
lightView.Details = new DetailInView[0];
Expand Down Expand Up @@ -848,8 +850,51 @@ private static void AddObjectToUpdate(List<DataObject> objsToUpdate, DataObject
objsToUpdate.Insert(0, dataObject); // Добавляем объект в начало списка.
}

}
}
}

/// <summary>
/// Получить значение ключа у указанной сущности.
/// </summary>
/// <param name="edmEntity">Сущность.</param>
/// <returns>Значение ключа.</returns>
private object GetKey(EdmEntityObject edmEntity)
{
if (edmEntity == null)
{
throw new ArgumentNullException(nameof(edmEntity), $"{nameof(edmEntity)} can not be null.");
}

object key;

// Получим значение ключа.
IEdmEntityType entityType = (IEdmEntityType)edmEntity.ActualEdmType;
IEnumerable<IEdmProperty> entityProps = entityType.Properties();
var keyProperty = entityProps.FirstOrDefault(prop => prop.Name == _model.KeyPropertyName);
edmEntity.TryGetPropertyValue(keyProperty.Name, out key);

return key;
}

/// <summary>
/// Построение объекта данных по сущности OData без загрузки свойств и мастеров/детейлов. Загружен только первичный ключ.
/// </summary>
/// <param name="edmEntity">Сущность OData.</param>
/// <returns>Объект данных.</returns>
private DataObject GetDataObjectByEdmEntityLight(EdmEntityObject edmEntity)
{
if (edmEntity == null)
{
throw new ArgumentNullException(nameof(edmEntity), $"{nameof(edmEntity)} can not be null.");
}

var masterType = _model.GetDataObjectType(edmEntity);
var masterKey = GetKey(edmEntity);
var dataObject = (DataObject)Activator.CreateInstance(masterType);
dataObject.SetExistObjectPrimaryKey(masterKey);

return dataObject;
}

/// <summary>
/// Построение объекта данных по сущности OData.
Expand All @@ -867,21 +912,7 @@ private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object ke
return null;
}

// Значение свойства.
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)
{
value = key;
}
else
{
edmEntity.TryGetPropertyValue(keyProperty.Name, out value);
}
key = key ?? GetKey(edmEntity);

// Загрузим объект из хранилища, если он там есть, или создадим, если нет, но только для POST.
// Тем самым гарантируем загруженность свойств при необходимости обновления и установку нужного статуса.
Expand All @@ -896,7 +927,7 @@ private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object ke
view = _model.GetDataObjectDefaultView(objType);
}

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

// Добавляем объект в список для обновления, если там ещё нет объекта с таким ключом.
AddObjectToUpdate(dObjs, obj, endObject);
Expand All @@ -906,6 +937,8 @@ private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object ke
IEnumerable<string> changedPropNames = edmEntity.GetChangedPropertyNames();

// Обрабатываем агрегатор первым.
IEdmEntityType entityType = (IEdmEntityType)edmEntity.ActualEdmType;
IEnumerable<IEdmProperty> entityProps = entityType.Properties().ToList();
List<IEdmProperty> changedProps = entityProps
.Where(ep => changedPropNames.Contains(ep.Name))
.OrderBy(ep => ep.Name != agregatorPropertyName)
Expand All @@ -931,22 +964,37 @@ private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object ke
// Обработка мастеров и детейлов.
if (prop is EdmNavigationProperty navProp)
{
edmEntity.TryGetPropertyValue(prop.Name, out value);

EdmMultiplicity edmMultiplicity = navProp.TargetMultiplicity();

// Обработка мастеров.
if (edmMultiplicity == EdmMultiplicity.One || edmMultiplicity == EdmMultiplicity.ZeroOrOne)
{
object value;
edmEntity.TryGetPropertyValue(prop.Name, out value);

if (value is EdmEntityObject edmMaster)
{
// Порядок вставки влияет на порядок отправки объектов в UpdateObjects это в свою очередь влияет на то, как срабатывают бизнес-серверы. Бизнес-сервер мастера должен сработать после, а агрегатора перед этим объектом.
bool insertIntoEnd = string.IsNullOrEmpty(agregatorPropertyName);
DataObject master = GetDataObjectByEdmEntity(edmMaster, null, dObjs, insertIntoEnd, useUpdateView);
bool masterOwnPropsUpdated = edmMaster.GetChangedPropertyNames().Any(propName => propName != _model.KeyPropertyName);
bool isAggregator = dataObjectPropName == agregatorPropertyName;
DataObject master = null;

Type masterType = _model.GetDataObjectType(edmEntity);
bool masterLightLoad = _model.IsMasterLightLoad(masterType);

if (masterLightLoad && !masterOwnPropsUpdated && !isAggregator)
{
master = GetDataObjectByEdmEntityLight(edmMaster); // здесь мастер не добавляется в dObjs (объекты на обновление) т.к. мы точно знаем что он будет в состоянии UnAltered
}
else
{
master = GetDataObjectByEdmEntity(edmMaster, null, dObjs, insertIntoEnd, useUpdateView);
}

Information.SetPropValueByName(obj, dataObjectPropName, master);

if (dataObjectPropName == agregatorPropertyName)
if (isAggregator)
{
master.AddDetail(obj);

Expand All @@ -969,6 +1017,9 @@ private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object ke
{
DetailArray detarr = (DetailArray)Information.GetPropValueByName(obj, dataObjectPropName);

object value;
edmEntity.TryGetPropertyValue(prop.Name, out value);

if (value is EdmEntityObjectCollection coll)
{
if (coll != null && coll.Count > 0)
Expand Down Expand Up @@ -1002,11 +1053,13 @@ private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object ke
else
{
// Обработка собственных свойств объекта (неключевых, т.к. ключ устанавливаем при начальной инициализации объекта obj).
if (prop.Name != keyProperty.Name)
if (prop.Name != _model.KeyPropertyName)
{
Type dataObjectPropertyType = Information.GetPropertyType(objType, dataObjectPropName);
object value;
edmEntity.TryGetPropertyValue(prop.Name, out value);

Type dataObjectPropertyType = Information.GetPropertyType(objType, dataObjectPropName);

// Если тип свойства относится к одному из зарегистрированных провайдеров файловых свойств,
// значит свойство файловое, и его нужно обработать особым образом.
if (_dataObjectFileAccessor.HasDataObjectFileProvider(dataObjectPropertyType))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,13 @@ public View GetDataObjectUpdateView(Type dataObjectType)
return _metadata[dataObjectType].UpdateView?.Clone();
}

/// <summary>
/// Возвращает информацию, должны ли мастера объекта загружаться в экономном режиме (только __PrimaryKey).
/// </summary>
/// <param name="dataObjectType">Тип объекта данных.</param>
/// <returns>Мастера должны загружаться экономно.</returns>
public bool IsMasterLightLoad(Type dataObjectType) => _metadata[dataObjectType].MasterLightLoad;

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

/// <summary>
/// Whether to load object masters in LightLoaded state (load only primary key).
/// </summary>
public bool MasterLightLoad { get; set; }

/// <summary>
/// The list of exposed details.
/// </summary>
Expand All @@ -56,4 +61,4 @@ public sealed class DataObjectEdmTypeSettings
/// </summary>
public IDictionary<PropertyInfo, DataObjectEdmDetailSettings> PseudoDetailProperties { get; } = new Dictionary<PropertyInfo, DataObjectEdmDetailSettings>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ public class DefaultDataObjectEdmModelBuilder : IDataObjectEdmModelBuilder
/// </summary>
private Dictionary<Type, View> UpdateViews { get; set; }

/// <summary>
/// Types for which masters should be light-loaded on updates (load only __PrimaryKey).
/// </summary>
private IEnumerable<Type> MasterLightLoadTypes { get; set; }

/// <summary>
/// Whether to load all masters in light-loaded mode on updates (load only __PrimaryKey).
/// </summary>
private bool MasterLightLoadAllTypes { get; set; }

private readonly PropertyInfo _keyProperty = Information.ExtractPropertyInfo<DataObject>(n => n.__PrimaryKey);

/// <summary>
Expand All @@ -88,7 +98,9 @@ public class DefaultDataObjectEdmModelBuilder : IDataObjectEdmModelBuilder
bool useNamespaceInEntitySetName = true,
PseudoDetailDefinitions pseudoDetailDefinitions = null,
Dictionary<Type, IEdmPrimitiveType> additionalMapping = null,
IEnumerable<KeyValuePair<Type, View>> updateViews = null)
IEnumerable<KeyValuePair<Type, View>> updateViews = null,
IEnumerable<Type> masterLightLoadTypes = null,
bool masterLightLoadAllTypes = false)
{
_searchAssemblies = searchAssemblies ?? throw new ArgumentNullException(nameof(searchAssemblies), "Contract assertion not met: searchAssemblies != null");
_useNamespaceInEntitySetName = useNamespaceInEntitySetName;
Expand All @@ -105,6 +117,18 @@ public class DefaultDataObjectEdmModelBuilder : IDataObjectEdmModelBuilder
{
SetUpdateView(updateViews);
}

if (masterLightLoadTypes != null)
{
SetMasterLightLoadTypes(masterLightLoadTypes);

if (masterLightLoadAllTypes)
{
System.Diagnostics.Debug.WriteLine("Detected usage of masterLightLoadAllTypes parameter together with masterLightLoadTypes in DefaultDataObjectEdmModelBuilder. masterLightLoadTypes will be ignored, all data objects will be loaded in MasterLightLoad mode.");
}
}

this.MasterLightLoadAllTypes = masterLightLoadAllTypes;
}

/// <summary>
Expand Down Expand Up @@ -215,7 +239,12 @@ private void SetUpdateView(Type dataObjectType, View updateView)
{
if (!dataObjectType.IsSubclassOf(typeof(DataObject)))
{
throw new ArgumentException("Update view can be set only for a DataObject.", nameof(dataObjectType));
throw new ArgumentException($"Update view can be set only for a DataObject. Current type is {dataObjectType}", nameof(dataObjectType));
}

if (dataObjectType is null)
{
throw new ArgumentException("dataObjectType can not be null.", nameof(dataObjectType));
}

if (updateView is null)
Expand All @@ -232,6 +261,26 @@ private void SetUpdateView(Type dataObjectType, View updateView)
UpdateViews[dataObjectType] = updateView;
}

/// <summary>
/// Sets DataObject types for which masters will be light-loaded on updates (load only __PrimaryKey).
/// </summary>
/// <param name="masterLightLoadTypes">Types for which masters should be light-loaded.</param>
private void SetMasterLightLoadTypes(IEnumerable<Type> masterLightLoadTypes)
{
if (masterLightLoadTypes != null)
{
foreach (Type type in masterLightLoadTypes)
{
if (!type.IsSubclassOf(typeof(DataObject)))
{
throw new ArgumentException("MasterLightLoad option can be set only for a DataObject.", nameof(masterLightLoadTypes));
}
}

MasterLightLoadTypes = masterLightLoadTypes;
}
}

/// <summary>
/// Adds the property for exposing.
/// </summary>
Expand Down Expand Up @@ -310,6 +359,7 @@ private void AddDataObjectWithHierarchy(DataObjectEdmMetadata meta, Type dataObj
CollectionName = EntitySetNameBuilder(dataObjectType),
DefaultView = DynamicView.Create(dataObjectType, null).View,
UpdateView = updateView,
MasterLightLoad = MasterLightLoadAllTypes || (MasterLightLoadTypes?.Contains(dataObjectType) ?? false),
};

AddProperties(dataObjectType, typeSettings);
Expand Down Expand Up @@ -458,4 +508,4 @@ private string BuildEntityPropertyName(PropertyInfo propertyDataObject)
return propertyDataObject.Name;
}
}
}
}
Loading

0 comments on commit 3197b6a

Please sign in to comment.