Skip to content

Commit

Permalink
Исправлена проблема с облегчённой загрузкой мастеров в batch запросе
Browse files Browse the repository at this point in the history
  • Loading branch information
inaidanov committed Mar 26, 2024
1 parent dbe2e01 commit 845394a
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ namespace NewPlatform.Flexberry.ORM.ODataService.Controllers
using NewPlatform.Flexberry.ORM.ODataService.Events;
using NewPlatform.Flexberry.ORM.ODataService.Handlers;
using Newtonsoft.Json.Linq;
using System.Web.UI.WebControls;
using View = ICSSoft.STORMNET.View;
#endif
#if NETSTANDARD
using System.Data;
Expand Down Expand Up @@ -742,12 +744,11 @@ private DataObject UpdateObject(EdmEntityObject edmEntity, object key)
}

/// <summary>
/// Загрузить существующий объект данных, используя EdmEntity.
/// Загрузить существующий объект данных в облегчённом варианте (только __PrimaryKey), используя информацию из EdmEntity.
/// </summary>
/// <param name="edmEntity">EdmEntity, который будет использован для получения объекта данных.</param>
/// <param name="view">Представление, по которому будет загружен объект (если не указано, будет загружен только __PrimaryKey).</param>
/// <returns>Объект данных.</returns>
private DataObject SafeLoadObject(EdmEntityObject edmEntity, View view = null)
private DataObject LightLoadDataObject(EdmEntityObject edmEntity)
{
if (edmEntity == null)
{
Expand All @@ -756,10 +757,54 @@ private DataObject SafeLoadObject(EdmEntityObject edmEntity, View view = null)

Type masterType = _model.GetDataObjectType(edmEntity);
object masterKey = GetKey(edmEntity);
View view = new View(new ViewAttribute("dynView", new string[] { Information.ExtractPropertyPath<DataObject>(x => x.__PrimaryKey) }), masterType);

if (view == null)
return LoadDataObject(masterType, masterKey, view);
}

/// <summary>
/// Загрузить существующий объект данных.
/// </summary>
/// <param name="objType">Тип загружаемого объекта.</param>
/// <param name="keyValue">Первичный ключ загружаемого объекта.</param>
/// <param name="view">Представление, по которому будет загружен объект.</param>
/// <returns>Объект данных.</returns>
private DataObject LoadDataObject(Type objType, object keyValue, View view)
{
DataObject dataObjectFromCache = DataObjectCache.GetLivingDataObject(objType, keyValue);

if (dataObjectFromCache != null)
{
view = new View(new ViewAttribute("dynView", new string[] { Information.ExtractPropertyPath<DataObject>(x => x.__PrimaryKey) }), masterType);
// Если объект не новый и не загружен целиком (начиная с [email protected]).
if (dataObjectFromCache.GetStatus(false) == ObjectStatus.UnAltered
&& dataObjectFromCache.GetLoadingState() != LoadingState.Loaded)
{
// Для обратной совместимости сравним перечень загруженных свойств и свойств в представлении.
/* Данный код срабатывает, например, если в кэше был объект, который загрузился только на уровне первичного ключа.
*
* Данный код также срабатывает в следующей ситуации: есть класс А, у него детейл Б, у которого есть наследник В.
* При загрузке объекта класса А подгрузятся его детейлы, однако они будут подгружены по представлению, которое соответствует классу Б, даже если детейлы класса В.
* Таким образом, в кэше окажутся объекты класса В, которые загружены только по свойствам Б. Раз не все свойства подгружены, то состояние LightLoaded.
* Догружать необходимо только те свойства, что ещё не загружались (потому что загруженные уже могут быть изменены).
*/
string[] loadedProps = dataObjectFromCache.GetLoadedProperties();
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.SafeLoadWithMasters(miniView, dataObjectFromCache, DataObjectCache);

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

return dataObjectFromCache;
}

// Вычитывать объект сразу с детейлами нельзя, поскольку в этой же транзакции могут уже оказаться отдельные операции с детейлами и перевычитка затрёт эти изменения.
Expand All @@ -768,8 +813,8 @@ private DataObject SafeLoadObject(EdmEntityObject edmEntity, View view = null)
lightView.Details = new DetailInView[0];

// Проверим существование объекта в базе.
LoadingCustomizationStruct lcs = LoadingCustomizationStruct.GetSimpleStruct(masterType, lightView);
lcs.LimitFunction = FunctionBuilder.BuildEquals(masterKey);
LoadingCustomizationStruct lcs = LoadingCustomizationStruct.GetSimpleStruct(objType, lightView);
lcs.LimitFunction = FunctionBuilder.BuildEquals(keyValue);
lcs.ReturnTop = 2;
DataObject[] dobjs = _dataService.LoadObjects(lcs, DataObjectCache);
if (dobjs.Length == 1)
Expand Down Expand Up @@ -803,62 +848,9 @@ private DataObject ReturnDataObject(Type objType, object keyValue)
if (keyValue != null)
{
View view = _model.GetDataObjectUpdateView(objType) ?? _model.GetDataObjectDefaultView(objType);

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

if (dataObjectFromCache != null)
DataObject dataObject = LoadDataObject(objType, keyValue, view);
if (dataObject != null)
{
// Если объект не новый и не загружен целиком (начиная с [email protected]).
if (dataObjectFromCache.GetStatus(false) == ObjectStatus.UnAltered
&& dataObjectFromCache.GetLoadingState() != LoadingState.Loaded)
{
// Для обратной совместимости сравним перечень загруженных свойств и свойств в представлении.
/* Данный код срабатывает, например, если в кэше был объект, который загрузился только на уровне первичного ключа.
*
* Данный код также срабатывает в следующей ситуации: есть класс А, у него детейл Б, у которого есть наследник В.
* При загрузке объекта класса А подгрузятся его детейлы, однако они будут подгружены по представлению, которое соответствует классу Б, даже если детейлы класса В.
* Таким образом, в кэше окажутся объекты класса В, которые загружены только по свойствам Б. Раз не все свойства подгружены, то состояние LightLoaded.
* Догружать необходимо только те свойства, что ещё не загружались (потому что загруженные уже могут быть изменены).
*/
string[] loadedProps = dataObjectFromCache.GetLoadedProperties();
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.SafeLoadWithMasters(miniView, dataObjectFromCache, DataObjectCache);

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

return dataObjectFromCache;
}

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

// Проверим существование объекта в базе.
LoadingCustomizationStruct lcs = LoadingCustomizationStruct.GetSimpleStruct(objType, lightView);
lcs.LimitFunction = FunctionBuilder.BuildEquals(keyValue);
lcs.ReturnTop = 2;
DataObject[] dobjs = _dataService.LoadObjects(lcs, DataObjectCache);
if (dobjs.Length == 1)
{
DataObject dataObject = dobjs[0];
if (lightViewDetails.Any())
{
// Дочитаем детейлы, чтобы в бизнес-серверах эти данные уже были. Детейлы с изменёнными состояниями будут пропущены из зачитки.
_dataService.SafeLoadDetails(view, new DataObject[] { dataObject }, DataObjectCache);
}

return dataObject;
}
}
Expand Down Expand Up @@ -1003,7 +995,8 @@ private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object ke

if (masterLightLoad && !masterOwnPropsUpdated && !isAggregator)
{
master = SafeLoadObject(edmMaster); // здесь мастер не добавляется в dObjs (объекты на обновление) т.к. мы точно знаем что он будет в состоянии UnAltered
master = LightLoadDataObject(edmMaster);
//AddObjectToUpdate(dObjs, master, insertIntoEnd);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public void MasterPropsChangedBatchTest()
string котенокJsonData = котенок.ToJson(котенокDynamicView, args.Token.Model);
// Добавляем в payload информацию о ссылке на мастера
котенокJsonData = ODataTestHelper.AddEntryRelationship(котенокJsonData, котенокDynamicView, args.Token.Model, кошка, nameof(Котенок.Кошка));
котенокJsonData = ODataTestHelper.AddEntryRelationship(котенокJsonData, котенокDynamicView, args.Token.Model, котенок.Кошка, nameof(Котенок.Кошка));
const string baseUrl = "http://localhost/odata";
string[] changesets = new[] // Важно, чтобы сначала шёл мастер, потом объект, имеющий на него ссылку.
Expand Down

0 comments on commit 845394a

Please sign in to comment.