Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Нежадная загрузка мастеров при обновлении объекта #293

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,13 +23,16 @@

#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
#if NETSTANDARD
using System.Data;
using Microsoft.AspNet.OData.Formatter;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
Expand Down Expand Up @@ -739,13 +742,31 @@ private DataObject UpdateObject(EdmEntityObject edmEntity, object key)
}

/// <summary>
/// Получить объект данных по ключу: если объект есть в хранилище, то возвращается загруженным по представлению <paramref name="view"/>, иначе - создаётся новый.
/// Получить объект данных на основании EdmEntity.
/// </summary>
/// <param name="edmEntity">EdmEntity, который будет использован для получения объекта данных.</param>
/// <returns>Объект данных.</returns>
private DataObject ReturnDataObject(EdmEntityObject edmEntity)
{
if (edmEntity == null)
{
throw new ArgumentNullException(nameof(edmEntity));
}

Type masterType = _model.GetDataObjectType(edmEntity);
object masterKey = GetKey(edmEntity);

return ReturnDataObject(masterType, masterKey);
}

/// <summary>
/// Получить объект данных по ключу: если объект есть в хранилище, то возвращается загруженным по представлению по умолчанию, иначе - создаётся новый.
/// </summary>
/// <param name="objType">Тип объекта, не может быть <c>null</c>.</param>
/// <param name="keyValue">Значение ключа.</param>
/// <param name="view">Представление для загрузки объекта.</param>
/// <param name="useUpdateView">Использовать UpdateView для загрузки объекта (при наличии).</param>
/// <returns>Объект данных.</returns>
private DataObject ReturnDataObject(Type objType, object keyValue, View view)
private DataObject ReturnDataObject(Type objType, object keyValue, bool useUpdateView = false)
{
if (objType == null)
{
Expand All @@ -754,6 +775,16 @@ private DataObject ReturnDataObject(Type objType, object keyValue, View view)

if (keyValue != null)
{
View view = null;
if (useUpdateView)
{
view = _model.GetDataObjectUpdateView(objType) ?? _model.GetDataObjectDefaultView(objType);
}
else
{
view = _model.GetDataObjectDefaultView(objType);
}

DataObject dataObjectFromCache = DataObjectCache.GetLivingDataObject(objType, keyValue);

if (dataObjectFromCache != null)
Expand All @@ -774,7 +805,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 +821,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 +879,31 @@ 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)
Anisimova2020 marked this conversation as resolved.
Show resolved Hide resolved
{
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);
Anisimova2020 marked this conversation as resolved.
Show resolved Hide resolved

return key;
}

/// <summary>
/// Построение объекта данных по сущности OData.
Expand All @@ -867,36 +921,13 @@ 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.
// Тем самым гарантируем загруженность свойств при необходимости обновления и установку нужного статуса.
Type objType = _model.GetDataObjectType(edmEntity);

View view = null;
if (useUpdateView)
{
view = _model.GetDataObjectUpdateView(objType) ?? _model.GetDataObjectDefaultView(objType);
} else
{
view = _model.GetDataObjectDefaultView(objType);
}

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

// Добавляем объект в список для обновления, если там ещё нет объекта с таким ключом.
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 objectType = _model.GetDataObjectType(edmEntity);
bool masterLightLoad = _model.IsMasterLightLoad(objectType);

if (masterLightLoad && !masterOwnPropsUpdated && !isAggregator)
{
master = ReturnDataObject(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;
Anisimova2020 marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Получает список зарегистрированных в модели типов по списку имён типов.
/// </summary>
Expand Down
Anisimova2020 marked this conversation as resolved.
Show resolved Hide resolved
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; }
Anisimova2020 marked this conversation as resolved.
Show resolved Hide resolved

/// <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>();
}
}
}
Loading
Loading