Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…exberry.ORM.ODataService into feature-239-use-proper-di
  • Loading branch information
hvostya committed Mar 28, 2024
2 parents 8fec6f6 + 7fb4f45 commit 934450a
Show file tree
Hide file tree
Showing 129 changed files with 10,798 additions and 7,846 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/sonar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ jobs:
name: Build and analyze
runs-on: windows-latest
steps:
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v1
with:
java-version: 1.11
java-version: '17'
- uses: actions/checkout@v2
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
Expand Down
19 changes: 14 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Flexberry ORM ODataService Changelog
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added
Expand All @@ -16,9 +15,19 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Fixed

## [7.1.1] - 2023.06.08
## [7.2.0] - 2024.03.27

### 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).

### Changed
1. Updated Flexberry ORM up to 7.2.0.

### Fixed
1. Fixed loading of object with crushing of already loaded masters.
2. Fixed loading of details.

## [7.1.1] - 2023.06.08

### Changed
1. Updated `NewPlatform.Flexberry.ORM` up to `7.1.1`.
Expand Down Expand Up @@ -111,7 +120,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
3. Support for limits on master details.
4. Support for limits on pseudodetails.
5. Decode Excel export column name.
6. HttpConfiguretion MapDataObjectRoute() extension method.
6. HttpConfiguretion MapDataObjectRoute() extension method.

### Changed

Expand Down Expand Up @@ -165,13 +174,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
3. Add support actions.
4. Add handler, called after exception appears.
5. In user functions and actions add possibility to return collections of primitive types and enums. In actions add possibility to use primitive types and enums as parameters.

### Fixed
1. Fix reading properties of files.
2. Fix error which occured in Mono in method `DefaultODataPathHandler.Parse(IEdmModel model, string serviceRoot, string odataPath)`.
3. Fix errors in work of user functions.
4. Fix error in association object enumeration filtration.

### Changed
1. Update dependencies.
2. Update ODataService package version to according ORM package version.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NewPlatform.Flexberry.ORM" Version="7.2.0-alpha02" />
<PackageReference Include="NewPlatform.Flexberry.ORM" Version="8.0.0-beta01" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NewPlatform.Flexberry.LockService" Version="3.0.0" />
<PackageReference Include="NewPlatform.Flexberry.ORM" Version="7.2.0-alpha02" />
<PackageReference Include="NewPlatform.Flexberry.ORM" Version="8.0.0-beta01" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
230 changes: 115 additions & 115 deletions NewPlatform.Flexberry.ORM.ODataService.nuspec

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,53 @@ public static object Execute(this SQLDataService dataService, Type dataObjectTyp
return queryProvider.Execute(expression);
}

/// <summary>
/// Загрузка объекта с его мастерами (объект должен быть не изменнённый и не до конца загруженный).
/// С мастерами необходимо обращаться аккуратно: если в кэше уже есть мастер, то нужно эту ситуацию разрешить,
/// поскольку иначе стандартная загрузка перетрёт данные мастера в кэше (и если он там изменён, то все изменения будут потеряны).
/// </summary>
/// <param name="dataService">Экземпляр сервиса данных.</param>
/// <param name="view">Представление объекта с мастерами.</param>
/// <param name="dobjectFromCache">Объект данных, в который будет производиться загрузка.</param>
/// <param name="dataObjectCache">Текущий кэш объектов данных (в данном кэше ранее существующие там объекты не должны быть перетёрты).</param>
public static void SafeLoadWithMasters(
this IDataService dataService, View view, ICSSoft.STORMNET.DataObject dobjectFromCache, DataObjectCache dataObjectCache)
{
if (dataService == null)
{
throw new ArgumentNullException(nameof(dataService));
}

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

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

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

// Прогружается пустой объект, чтобы избежать риска перетирания основного.
DataObject createdObject = (DataObject)Activator.CreateInstance(dobjectFromCache.GetType());
createdObject.SetExistObjectPrimaryKey(dobjectFromCache.__PrimaryKey);

// Используется отдельный кэш, чтобы не перетереть данные в основном кэше.
DataObjectCache specialCache = new DataObjectCache();
specialCache.StartCaching(false);
specialCache.AddDataObject(createdObject);
dataService.LoadObject(view, createdObject, false, true, specialCache);
specialCache.StopCaching();

// Перенос данных из одного объекта в другой.
ProperUpdateOfObject(dobjectFromCache, createdObject, dataObjectCache, specialCache);
}

/// <summary>
/// Загрузка детейлов с сохранением состояния изменения.
/// </summary>
Expand Down Expand Up @@ -122,28 +169,28 @@ public static void SafeLoadDetails(this IDataService dataService, View view, ILi
lcs.LimitFunction = FunctionBuilder.BuildIn(agregatorPropertyName, SQLWhereLanguageDef.LanguageDef.GetObjectTypeForNetType(agregatorKeyType), keys);

// Нужно соблюсти единственность инстанций агрегаторов при вычитке, поэтому реализуем отдельный кеш. Смешивать с кешем dataObjectCache нельзя, поскольку в предстоящей выборке будут те же самые детейлы (значения в кеше затрутся).
// Агрегаторы в кэш не помещаем. От помещения агрегаторов в кэш возникают неконтролируемые сбои основного кэша.
DataObjectCache agregatorCache = new DataObjectCache();
agregatorCache.StartCaching(false);
foreach (DataObject agregator in agregators)
{
agregatorCache.AddDataObject(agregator);
}

// Вычитываются детейлы одного типа, но для нескольких инстанций агрегаторов (оптимизируем количество SQL-запросов).
DataObject[] loadedDetails = dataService.LoadObjects(lcs, agregatorCache);
agregatorCache.StopCaching();

Dictionary<object, DataObject> extraCacheForAgregators = new Dictionary<object, DataObject>();
foreach (DataObject agregator in agregators)
{
agregator.AddLoadedProperties(detailInView.Name);
extraCacheForAgregators.Add(agregator.__PrimaryKey, agregator);
}

// Ввиду того, что агрегаторы нам пришли готовые с пустыми коллекциями детейлов, заполняем детейлы по агрегаторам значениями из кеша или из базы.
// На загрузку детейлов второго уровня передаем только детейлы отсутствующие в кэше.
List<DataObject> toLoadSecondDetails = new List<DataObject>();
foreach (DataObject loadedDetail in loadedDetails)
{
DataObject agregator = (DataObject)Information.GetPropValueByName(loadedDetail, agregatorPropertyName);
DataObject agregatorTemp = (DataObject)Information.GetPropValueByName(loadedDetail, agregatorPropertyName);
DataObject agregator = extraCacheForAgregators[agregatorTemp.__PrimaryKey];
object detailPrimaryKey = loadedDetail.__PrimaryKey;

DataObject detailFromCache = dataObjectCache.GetLivingDataObject(loadedDetail.GetType(), detailPrimaryKey);
Expand Down Expand Up @@ -179,6 +226,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 Expand Up @@ -226,5 +304,116 @@ public static void AddDetail(this DataObject agregator, DataObject detail)
LogService.LogWarn($"Detail type {detailType.AssemblyQualifiedName} not found in agregator of type {agregatorType.AssemblyQualifiedName}.");
}
}


/// <summary>
/// Перенос означенных свойств из свежезагруженного объекта в основной, расположенный в основном кэше.
/// </summary>
/// <param name="currentObject">Основной объект, куда необходимо копировать значения свойств.</param>
/// <param name="loadedObjectLocal">Свежезагруженный объект.</param>
/// <param name="dataObjectCache">Основной кэш.</param>
/// <param name="dataObjectCacheLocal">Локальный кэш, куда была выполнена свежая прогрузка.</param>
private static void ProperUpdateOfObject(DataObject currentObject, DataObject loadedObjectLocal, DataObjectCache dataObjectCache, DataObjectCache dataObjectCacheLocal)
{
if (currentObject == null)
{
throw new ArgumentNullException(nameof(currentObject));
}

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

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

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

// Перенос значений свойств объекта (в том числе могут быть мастера). Если мастера означены, то перенос свойств мастера производится далее.
List<string> localObjectLoadedProps = loadedObjectLocal.GetLoadedPropertiesList();
List<string> currentObjectLoadedProps = currentObject.GetLoadedPropertiesList();
List<string> notLoadedForActual = localObjectLoadedProps.Except(currentObjectLoadedProps).ToList();
DataObject currentDataCopy = currentObject.GetDataCopy();
foreach (string notLoadedPropName in notLoadedForActual)
{
object propValue = Information.GetPropValueByName(loadedObjectLocal, notLoadedPropName);
Information.SetPropValueByName(currentObject, notLoadedPropName, propValue);
currentObject.AddLoadedProperties(notLoadedPropName);
Information.SetPropValueByName(currentDataCopy, notLoadedPropName, propValue);
}

// Ещё могут быть частично загруженные мастера.
ProperCacheUpdateForOneObject(dataObjectCache, dataObjectCacheLocal, loadedObjectLocal, true);
}

/// <summary>
/// Обновление кэша по свежезагруженному объекту.
/// </summary>
/// <param name="dataObjectCacheActual">Текущий основной кэш объектов.</param>
/// <param name="dataObjectCacheWithMasters">Вспомогательный кэш, куда загружался объект.</param>
/// <param name="loadedDataObject">Свежезагруженный объект, по которому обновляется основной кэш.</param>
/// <param name="loadedObjectsAdded">Флаг, определяющий, что в кэш уже добавлен свежезагруженный объект.</param>
private static void ProperCacheUpdateForOneObject(DataObjectCache dataObjectCacheActual, DataObjectCache dataObjectCacheWithMasters, DataObject loadedDataObject, bool loadedObjectsAdded)
{
if (dataObjectCacheActual == null)
{
throw new ArgumentNullException(nameof(dataObjectCacheActual));
}

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

if (loadedDataObject == null)
{
return;
}

if (!loadedObjectsAdded)
{
dataObjectCacheActual.AddDataObject(loadedDataObject);
}

Type dobjType = typeof(DataObject);
Type currentType = loadedDataObject.GetType();
List<string> loadedProperties = loadedDataObject.GetLoadedPropertiesList();
foreach (string currentPropertyName in loadedProperties)
{
Type currentPropertyType = Information.GetPropertyType(currentType, currentPropertyName);
if (currentPropertyType.IsSubclassOf(dobjType)) // Выбираем у текущего объекта ссылки на мастеров.
{
DataObject currentMaster = (DataObject)Information.GetPropValueByName(loadedDataObject, currentPropertyName);
if (currentMaster != null)
{
// Типы currentPropertyType и currentMaster.GetType() могут различаться из-за наследования.
DataObject masterFromActualCache = dataObjectCacheActual.GetLivingDataObject(currentMaster.GetType(), currentMaster.__PrimaryKey);

if (masterFromActualCache == null)
{
// Если мастера ранее не было в кэше, то просто его туда переносим.
dataObjectCacheActual.AddDataObject(currentMaster);

// Но в добавленном мастере могут быть мастера 2 и далее уровней.
ProperCacheUpdateForOneObject(dataObjectCacheActual, dataObjectCacheWithMasters, currentMaster, true);
}
else
{ // Если мастер был в кэше, то аккуратно нужно перенести только незагруженные ранее свойства.
if (masterFromActualCache.GetStatus(false) == ObjectStatus.UnAltered && masterFromActualCache.GetLoadingState() != LoadingState.Loaded)
{
ProperUpdateOfObject(masterFromActualCache, currentMaster, dataObjectCacheActual, dataObjectCacheWithMasters);
}
}
}
}
}
}

}
}
Loading

0 comments on commit 934450a

Please sign in to comment.