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 17 commits into
base: develop
Choose a base branch
from

Conversation

turbcool
Copy link
Contributor

@turbcool turbcool commented Jan 29, 2024

#290, пункт 2. Мержить после #292.

Summary by CodeRabbit

  • New Features

    • Introduced new parameters for customizing OData update views and loading modes for master objects.
    • Added a method to check if masters should be loaded in a light mode.
    • New property to indicate light loading for object masters.
  • Bug Fixes

    • Improved error handling and response creation for OData operations.
    • Enhanced loading process for objects with previously loaded masters.
  • Tests

    • Added unit tests for the MasterLightLoad functionality to ensure correct behavior during updates.
  • Documentation

    • Updated changelog to reflect new features, changes, and fixes.

@Anisimova2020
Copy link
Contributor

Нужно проверить на тесте, который я добавила в develop-версию. Там хитрый батч-запрос и вот как раз мастера не меняются. Интересно, как новая логика пережуёт проблему.

Copy link

sonarcloud bot commented Mar 27, 2024

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarCloud

Catch issues before they fail your Quality Gate with our IDE extension SonarLint

@bratchikov bratchikov closed this Oct 23, 2024
@bratchikov bratchikov reopened this Oct 23, 2024
Copy link

coderabbitai bot commented Oct 23, 2024

Walkthrough

The changes in this pull request encompass updates to the Flexberry ORM ODataService, including the addition of new parameters and methods to enhance the handling of OData update requests. Key modifications include the introduction of parameters for controlling update views and loading modes, refactoring of the DataObjectController methods to align with ASP.NET Core practices, and the addition of unit tests for the new functionality. The changelog has been updated to reflect these changes, including version updates and documented fixes.

Changes

File Change Summary
CHANGELOG.md Updated to include new parameters updateViews, masterLightLoadTypes, and masterLightLoadAllTypes in DefaultDataObjectEdmModelBuilder. Documented version update to 7.2.0-beta01 and various fixes regarding loading processes.
NewPlatform.Flexberry.ORM.ODataService/Controllers/DataObjectController.ModifyData.cs Updated method signatures from HttpResponseMessage to IActionResult or NoContentResult. Refactored error handling to throw CustomException. Simplified internal logic for entity updates and deletions. Added new private methods for better readability. Expanded conditional compilation directives for framework compatibility.
NewPlatform.Flexberry.ORM.ODataService/Model/DataObjectEdmModel.cs Added new method IsMasterLightLoad to check if masters should be loaded in light mode based on metadata.
NewPlatform.Flexberry.ORM.ODataService/Model/DataObjectEdmTypeSettings.cs Added new boolean property MasterLightLoad to indicate light loading for object masters.
NewPlatform.Flexberry.ORM.ODataService/Model/DefaultDataObjectEdmModelBuilder.cs Introduced new properties MasterLightLoadTypes and MasterLightLoadAllTypes. Updated constructor to include these parameters and modified methods for better error handling and loading logic.
Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CRUD/Update/MasterLightLoadTest.cs Added unit tests for MasterLightLoad functionality, including tests for changing master entities and batch updates.
Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/Helpers/ODataTestHelper.cs Renamed variable objJsonМедв to objJson in AddEntryRelationship method for clarity.
Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/Startups/MasterLightLoadTestStartup.cs Introduced MasterLightLoadTestStartup class for testing configurations specific to MasterLightLoad. Overridden Configure method to set up middleware and OData service with light-load specifications.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ODataService
    participant DataObjectController
    participant DataObjectEdmModelBuilder

    Client->>ODataService: Send Update Request
    ODataService->>DataObjectController: Handle Request
    DataObjectController->>DataObjectEdmModelBuilder: Process Update
    DataObjectEdmModelBuilder->>DataObjectController: Return Result
    DataObjectController->>ODataService: Send Response
    ODataService->>Client: Return Response
Loading

🐰 In the meadow, changes bloom bright,
New features sprout, oh what a sight!
With light loads dancing, masters in tow,
The OData service now flows like a show!
Let’s hop with joy, for the code is refined,
In the world of updates, we’re one of a kind! 🌼✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🧹 Outside diff range and nitpick comments (11)
Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/Helpers/ODataTestHelper.cs (1)

23-32: Consider enhancing XML documentation for parameters.

While the method has a good summary, the parameter documentation could be more descriptive to better explain their purpose and expected format.

         /// <summary>
         /// Добавить запись об изменении ссылки у объекта в тело запроса к OData.
         /// </summary>
-        /// <param name="requestJsonData">Исходное тело запроса.</param>
-        /// <param name="view">Представление исходного объекта.</param>
-        /// <param name="model">EDM модель.</param>
-        /// <param name="dataObject">Новый объект данных (по ссылке).</param>
-        /// <param name="relationName">Ссылка на новый объект данных.</param>
+        /// <param name="requestJsonData">Исходное тело запроса в формате JSON.</param>
+        /// <param name="view">Представление исходного объекта, определяющее структуру данных.</param>
+        /// <param name="model">EDM модель, содержащая метаданные для OData сервиса.</param>
+        /// <param name="dataObject">Новый объект данных, на который будет установлена ссылка.</param>
+        /// <param name="relationName">Имя свойства, представляющего связь с новым объектом данных.</param>
         /// <returns>Новое тело запроса к OData.</returns>
NewPlatform.Flexberry.ORM.ODataService/Model/DataObjectEdmTypeSettings.cs (1)

39-41: Enhance the XML documentation for better clarity.

The documentation could be more comprehensive by including:

  • Use cases for when this setting should be enabled
  • Performance implications of light loading
  • Relationship with the UpdateView property
  • Example usage

Consider expanding the documentation like this:

 /// <summary>
-/// Whether to load object masters in LightLoaded state (load only primary key).
+/// Whether to load object masters in LightLoaded state (load only primary key).
+/// When enabled, improves performance by minimizing data fetching during updates.
+/// This is particularly useful for batch operations where master objects don't need
+/// to be fully loaded. Works in conjunction with UpdateView to optimize loading strategy.
+/// </summary>
+/// <remarks>
+/// Example: When updating a Task object that has a Project master, enabling this setting
+/// will only load the Project's primary key instead of all its properties, reducing
+/// unnecessary data transfer.
+/// </remarks>
 /// </summary>
Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/Startups/MasterLightLoadTestStartup.cs (2)

21-34: Enhance class and constructor documentation.

The documentation could be more detailed to help other developers understand:

  • What "light-loading" means in this context
  • The implications of marking a type for light-loading
  • The configuration parameter's purpose and expected values
 /// <summary>
-/// Startup for testing MasterLightLoad configuration.
-/// Differs from TestStartup that it marks <see cref="Котенок"/> type as data object for which masters should be light-loaded.
+/// Startup class for testing the MasterLightLoad configuration, which implements non-greedy loading of master objects.
+/// Unlike TestStartup, this class configures the <see cref="Котенок"/> type to use light-loading for its master objects,
+/// meaning that master objects are loaded with minimal required fields instead of full object graphs.
 /// </summary>
 public class MasterLightLoadTestStartup : Startup
 {
     /// <summary>
-    /// Initialize new instance of TestStartup.
+    /// Initializes a new instance of the <see cref="MasterLightLoadTestStartup"/> class.
     /// </summary>
-    /// <param name="configuration">Configuration for new instance.</param>
+    /// <param name="configuration">The configuration settings for the application, including any custom settings for master light-loading.</param>

68-71: Consider documenting the management token registration.

The token registration looks good, but its purpose and usage should be documented.

+// Register the OData route management token for dependency injection
 var token = builder.MapDataObjectRoute(modelBuilder);
 container.RegisterInstance(typeof(ManagementToken), token);
CHANGELOG.md (2)

9-9: Enhance the description of master loading parameters.

Consider expanding the description to better explain the purpose and impact of these parameters. For example:

-2. `masterLightLoadTypes` and `masterLightLoadAllTypes` parameters of `DefaultDataObjectEdmModelBuilder` class. They allow to change loading mode of masters during OData update requests.
+2. `masterLightLoadTypes` and `masterLightLoadAllTypes` parameters of `DefaultDataObjectEdmModelBuilder` class. They allow controlling how master objects are loaded during OData update requests, enabling non-greedy loading to improve performance.
🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Did you mean “changing”? Or maybe you should add a pronoun? In active voice, ‘allow’ + ‘to’ takes an object, usually a pronoun.
Context: ...bjectEdmModelBuilder` class. They allow to change loading mode of masters during OData up...

(ALLOW_TO)


Line range hint 1-1: Clarify the Fixed section entry about master loading.

The entry "Fixed loading of object with crushing of already loaded masters" is unclear. Consider rewording to better explain the issue that was fixed:

-1. Fixed loading of object with crushing of already loaded masters.
+1. Fixed an issue where loading objects would corrupt previously loaded master objects.
🧰 Tools
🪛 LanguageTool

[grammar] ~8-~8: Did you mean “changing”? Or maybe you should add a pronoun? In active voice, ‘allow’ + ‘to’ takes an object, usually a pronoun.
Context: ...ObjectEdmModelBuilder` class. It allows to change update views for data objects (update v...

(ALLOW_TO)


[grammar] ~9-~9: Did you mean “changing”? Or maybe you should add a pronoun? In active voice, ‘allow’ + ‘to’ takes an object, usually a pronoun.
Context: ...bjectEdmModelBuilder` class. They allow to change loading mode of masters during OData up...

(ALLOW_TO)

Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CRUD/Update/MasterLightLoadTest.cs (2)

19-23: Consider enhancing the class documentation.

While the documentation explains the purpose well, it would be beneficial to add:

  • Prerequisites for running these tests
  • Expected test environment configuration
  • Description of the MasterLightLoad feature being tested

1-233: Enhance test coverage for light loading verification.

While the tests cover the main scenarios (master change, property updates, and batch operations), they lack proper verification of the light loading behavior itself. Consider:

  1. Add negative test cases (when light loading should not occur)
  2. Add test cases for edge cases (null masters, circular references)
  3. Implement proper verification of the light loading state

This will ensure that the MasterLightLoad feature works as intended and remains stable across changes.

NewPlatform.Flexberry.ORM.ODataService/Model/DataObjectEdmModel.cs (1)

531-537: LGTM! Consider enhancing the documentation.

The implementation is clean and follows good practices with null-safe navigation. The method aligns well with the PR objective of implementing non-greedy loading for masters.

Consider enhancing the XML documentation to clarify what "экономном режиме" (economical mode) means in practice, for example:

 /// <summary>
-/// Возвращает информацию, должны ли мастера объекта загружаться в экономном режиме (только __PrimaryKey).
+/// Возвращает информацию, должны ли мастера объекта загружаться в экономном режиме.
+/// В экономном режиме для мастеров загружается только первичный ключ (__PrimaryKey),
+/// что позволяет оптимизировать загрузку данных при обновлении объекта.
 /// </summary>
NewPlatform.Flexberry.ORM.ODataService/Controllers/DataObjectController.ModifyData.cs (2)

751-753: Redundant null check for edmEntity in LightLoadDataObject

The null check for edmEntity in the LightLoadDataObject method may be redundant if all callers ensure that edmEntity is not null. If GetDataObjectByEdmEntity is updated to throw an exception when edmEntity is null, consider removing this redundant check to streamline the code.

Alternatively, if you prefer defensive programming, keep the null check for extra safety.


904-904: Correct the typo in exception message

There is a minor typo in the exception message at line 903. The phrase "can not" should be "cannot" for proper grammar.

Apply this diff to fix the typo:

                    throw new ArgumentNullException(nameof(edmEntity), $"{nameof(edmEntity)} can not be null.");
+                                                      $"{nameof(edmEntity)} cannot be null.");
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d68e938 and 21ac6cc.

📒 Files selected for processing (9)
  • CHANGELOG.md (1 hunks)
  • NewPlatform.Flexberry.ORM.ODataService/Controllers/DataObjectController.ModifyData.cs (11 hunks)
  • NewPlatform.Flexberry.ORM.ODataService/Model/DataObjectEdmModel.cs (1 hunks)
  • NewPlatform.Flexberry.ORM.ODataService/Model/DataObjectEdmTypeSettings.cs (2 hunks)
  • NewPlatform.Flexberry.ORM.ODataService/Model/DefaultDataObjectEdmModelBuilder.cs (7 hunks)
  • Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CRUD/Update/MasterLightLoadTest.cs (1 hunks)
  • Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CodeGen/NewPlatform.Flexberry.ORM.ODataService.Tests.crp (1 hunks)
  • Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/Helpers/ODataTestHelper.cs (1 hunks)
  • Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/Startups/MasterLightLoadTestStartup.cs (1 hunks)
🔥 Files not summarized due to errors (1)
  • Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CodeGen/NewPlatform.Flexberry.ORM.ODataService.Tests.crp: Error: Server error: no LLM provider could handle the message
🧰 Additional context used
🪛 LanguageTool
CHANGELOG.md

[grammar] ~9-~9: Did you mean “changing”? Or maybe you should add a pronoun? In active voice, ‘allow’ + ‘to’ takes an object, usually a pronoun.
Context: ...bjectEdmModelBuilder` class. They allow to change loading mode of masters during OData up...

(ALLOW_TO)

🔇 Additional comments (6)
Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/Helpers/ODataTestHelper.cs (1)

23-23: LGTM! Good improvement in variable naming.

The change from objJsonМедв to objJson improves code readability by using ASCII-only characters while maintaining the same functionality.

NewPlatform.Flexberry.ORM.ODataService/Model/DataObjectEdmTypeSettings.cs (1)

42-42: Property implementation looks good, verify integration.

The property design is appropriate for its purpose. The boolean type and public accessibility align well with the class design pattern.

Let's verify how this property is used across the codebase:

✅ Verification successful

Let's gather more specific information about how this property is being used by examining the actual implementations.


Based on the gathered information, I can now provide a final response as the implementation details are clear.

Property is well-integrated and properly used across the codebase

The MasterLightLoad property is correctly implemented and integrated:

  • Used in DataObjectController for controlling master object loading behavior during updates
  • Configured through DefaultDataObjectEdmModelBuilder with support for both type-specific and global settings
  • Thoroughly tested in MasterLightLoadTest with specific test cases for different scenarios:
    • Master object changes
    • Master field value updates
    • Integration with the service startup configuration
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the usage of MasterLightLoad property
# and its integration with related components

# Check for property usage in controllers and services
echo "Checking MasterLightLoad usage in controllers and services:"
rg -l "MasterLightLoad" --type cs

# Check for related configuration or builder patterns
echo "\nChecking for configuration setup:"
ast-grep --pattern 'new DataObjectEdmTypeSettings { $$$MasterLightLoad = $_,$$$ }'

Length of output: 944


Script:

#!/bin/bash
# Check the actual usage of MasterLightLoad in DataObjectController
echo "Checking MasterLightLoad usage in DataObjectController:"
rg "MasterLightLoad" "NewPlatform.Flexberry.ORM.ODataService/Controllers/DataObjectController.ModifyData.cs" -B 2 -A 2

# Check how it's used in the model builder
echo -e "\nChecking MasterLightLoad usage in DefaultDataObjectEdmModelBuilder:"
rg "MasterLightLoad" "NewPlatform.Flexberry.ORM.ODataService/Model/DefaultDataObjectEdmModelBuilder.cs" -B 2 -A 2

# Check test implementation for insights
echo -e "\nChecking test implementation:"
rg "MasterLightLoad" "Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CRUD/Update/MasterLightLoadTest.cs" -B 2 -A 2

Length of output: 3936

Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/Startups/MasterLightLoadTestStartup.cs (1)

54-61: Verify assembly references and document dependencies.

The assembly configuration looks good, but consider adding validation and documentation:

 var assemblies = new[]
 {
+    // Contains the Котенок entity and related types
     typeof(Котенок).Assembly,
+    // Contains application logging functionality
     typeof(ApplicationLog).Assembly,
+    // Contains user settings and preferences
     typeof(UserSetting).Assembly,
+    // Contains locking mechanism implementations
     typeof(Lock).Assembly,
 };
Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CRUD/Update/MasterLightLoadTest.cs (1)

76-79: Implement verification for light loading behavior.

The comment indicates that verification requires manual debugging. Consider these alternatives:

  1. Use reflection to verify the loading state
  2. Add assertions on the loaded properties to ensure only necessary data is loaded
  3. Implement an interceptor in the test startup to track loaded properties

Would you like me to help implement any of these verification approaches?

✅ Verification successful

Let me try a different search strategy to understand how light loading is implemented and tested in the codebase.

This should help us understand:

  1. How light loading is generally handled in the codebase
  2. If there are any existing test patterns we can follow
  3. What verification mechanisms are currently in place

Based on the search results, I can now provide a comprehensive response about the light loading verification:

Consider using GetLoadedProperties() to verify light loading state

The codebase reveals that light loading is implemented through the MasterLightLoad configuration and can be verified by checking the loaded properties state. Here's how to improve the test:

  • Use GetLoadedProperties() method to verify that only the primary key is loaded for the master object
  • Add assertions to check that other properties are not loaded

Example implementation:

// After the response status check
var loadedProps = master.GetLoadedProperties();
Assert.Single(loadedProps); // Only __PrimaryKey should be loaded
Assert.Contains("__PrimaryKey", loadedProps);

This approach is more reliable than manual debugging and aligns with how the framework internally handles light loading, as seen in DataObjectController.ModifyData.cs.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing implementations of light loading verification
rg -A 5 "LightLoad.*[Vv]erif"

Length of output: 31


Script:

#!/bin/bash
# Search for light loading related tests and implementations
echo "=== Searching for light loading tests ==="
rg -i "lightload" --type cs -A 5

echo -e "\n=== Searching for specific test patterns ==="
ast-grep --pattern 'class $_ {
  $$$
  [Test]
  public void $_() {
    $$$
    LightLoad
    $$$
  }
}'

Length of output: 22062

Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CodeGen/NewPlatform.Flexberry.ORM.ODataService.Tests.crp (1)

1335-1335: LGTM!

The XML closing tag is properly formatted and correctly closes the STORMNETXMLDataService root element.

NewPlatform.Flexberry.ORM.ODataService/Controllers/DataObjectController.ModifyData.cs (1)

992-1002: Verify the new logic for light loading of master objects

The logic for handling master objects with light loading has been introduced:

bool masterLightLoad = _model.IsMasterLightLoad(objectType);

if (masterLightLoad && !masterOwnPropsUpdated && !isAggregator)
{
    master = LightLoadDataObject(edmMaster);
}
else
{
    master = GetDataObjectByEdmEntity(edmMaster, null, dObjs, insertIntoEnd);
}

Ensure that this approach correctly handles all cases, especially when the master has been modified or when aggregation is involved, to prevent any unintended consequences or data inconsistencies.

Please run comprehensive tests covering various scenarios to confirm that master objects are properly loaded and updated according to the new logic.

Comment on lines +36 to +49
/// <inheritdoc/>
public override void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
IUnityContainer unityContainer = UnityFactory.GetContainer();
unityContainer.RegisterInstance(env);

app.UseMiddleware<ExceptionMiddleware>();

app.UseMvc(builder =>
{
builder.MapRoute("Lock", "api/lock/{action}/{dataObjectId}", new { controller = "Lock" });
builder.MapFileRoute();
});

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and route documentation.

The middleware and routing setup looks good, but consider these improvements:

  1. Add error handling for container resolution
  2. Document the purpose of each route
 public override void Configure(IApplicationBuilder app, IHostingEnvironment env)
 {
-    IUnityContainer unityContainer = UnityFactory.GetContainer();
+    IUnityContainer unityContainer;
+    try
+    {
+        unityContainer = UnityFactory.GetContainer();
+    }
+    catch (Exception ex)
+    {
+        throw new InvalidOperationException("Failed to initialize Unity container", ex);
+    }
     unityContainer.RegisterInstance(env);

     app.UseMiddleware<ExceptionMiddleware>();

     app.UseMvc(builder =>
     {
+        // Route for handling object locking operations
         builder.MapRoute("Lock", "api/lock/{action}/{dataObjectId}", new { controller = "Lock" });
+        // Route for serving static files
         builder.MapFileRoute();
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// <inheritdoc/>
public override void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
IUnityContainer unityContainer = UnityFactory.GetContainer();
unityContainer.RegisterInstance(env);
app.UseMiddleware<ExceptionMiddleware>();
app.UseMvc(builder =>
{
builder.MapRoute("Lock", "api/lock/{action}/{dataObjectId}", new { controller = "Lock" });
builder.MapFileRoute();
});
/// <inheritdoc/>
public override void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
IUnityContainer unityContainer;
try
{
unityContainer = UnityFactory.GetContainer();
}
catch (Exception ex)
{
throw new InvalidOperationException("Failed to initialize Unity container", ex);
}
unityContainer.RegisterInstance(env);
app.UseMiddleware<ExceptionMiddleware>();
app.UseMvc(builder =>
{
// Route for handling object locking operations
builder.MapRoute("Lock", "api/lock/{action}/{dataObjectId}", new { controller = "Lock" });
// Route for serving static files
builder.MapFileRoute();
});

Comment on lines +64 to +66
// Set MasterLightLoad property for this DataObject
var masterLightLoadTypes = new List<Type> { typeof(Котенок) };
var modelBuilder = new DefaultDataObjectEdmModelBuilder(assemblies, false, pseudoDetailDefinitions, masterLightLoadTypes: masterLightLoadTypes);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider validating masterLightLoadTypes configuration.

The light-load configuration looks correct, but consider adding validation to ensure the type exists in the referenced assemblies.

-// Set MasterLightLoad property for this DataObject
-var masterLightLoadTypes = new List<Type> { typeof(Котенок) }; 
-var modelBuilder = new DefaultDataObjectEdmModelBuilder(assemblies, false, pseudoDetailDefinitions, masterLightLoadTypes: masterLightLoadTypes);
+// Configure light-loading for master objects of Котенок type
+var masterLightLoadTypes = new List<Type> { typeof(Котенок) };
+
+// Validate that the light-load type exists in the referenced assemblies
+if (!assemblies.Any(a => a.GetTypes().Contains(typeof(Котенок))))
+{
+    throw new InvalidOperationException($"Type {typeof(Котенок).FullName} not found in the referenced assemblies");
+}
+
+var modelBuilder = new DefaultDataObjectEdmModelBuilder(
+    assemblies,
+    false,
+    pseudoDetailDefinitions,
+    masterLightLoadTypes: masterLightLoadTypes);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Set MasterLightLoad property for this DataObject
var masterLightLoadTypes = new List<Type> { typeof(Котенок) };
var modelBuilder = new DefaultDataObjectEdmModelBuilder(assemblies, false, pseudoDetailDefinitions, masterLightLoadTypes: masterLightLoadTypes);
// Configure light-loading for master objects of Котенок type
var masterLightLoadTypes = new List<Type> { typeof(Котенок) };
// Validate that the light-load type exists in the referenced assemblies
if (!assemblies.Any(a => a.GetTypes().Contains(typeof(Котенок))))
{
throw new InvalidOperationException($"Type {typeof(Котенок).FullName} not found in the referenced assemblies");
}
var modelBuilder = new DefaultDataObjectEdmModelBuilder(
assemblies,
false,
pseudoDetailDefinitions,
masterLightLoadTypes: masterLightLoadTypes);

@@ -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.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix grammatical issue in the parameter description.

The phrase "allow to change" is grammatically incorrect. Consider rewording:

-They allow to change loading mode of masters during OData update requests.
+They allow changing the loading mode of masters during OData update requests.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
2. `masterLightLoadTypes` and `masterLightLoadAllTypes` parameters of `DefaultDataObjectEdmModelBuilder` class. They allow to change loading mode of masters during OData update requests.
2. `masterLightLoadTypes` and `masterLightLoadAllTypes` parameters of `DefaultDataObjectEdmModelBuilder` class. They allow changing the loading mode of masters during OData update requests.
🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Did you mean “changing”? Or maybe you should add a pronoun? In active voice, ‘allow’ + ‘to’ takes an object, usually a pronoun.
Context: ...bjectEdmModelBuilder` class. They allow to change loading mode of masters during OData up...

(ALLOW_TO)

Comment on lines +130 to +140
string[] changesets = new[] // Важно, чтобы сначала шёл мастер, потом объект, имеющий на него ссылку.
{
CreateChangeset(
$"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(Кошка)).Name}",
кошка.ToJson(кошкаDynamicView, args.Token.Model),
кошка),
CreateChangeset(
$"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(Котенок)).Name}",
котенокJsonData,
котенок),
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Make the changeset ordering requirement more robust.

The comment indicates that master updates must precede dependent object updates. Consider:

  1. Add validation to enforce this ordering
  2. Document the requirement more prominently
  3. Add a test case that verifies the behavior when the order is incorrect

Example validation:

private void ValidateChangesetOrder(string[] changesets)
{
    // Verify that master updates come before dependent updates
    var masterIndex = Array.FindIndex(changesets, c => c.Contains("/Кошка"));
    var dependentIndex = Array.FindIndex(changesets, c => c.Contains("/Котенок"));
    if (masterIndex > dependentIndex && masterIndex != -1 && dependentIndex != -1)
    {
        throw new InvalidOperationException("Master updates must precede dependent updates in changesets");
    }
}

Comment on lines +186 to +191
string[] котенокPropertiesNames =
{
Information.ExtractPropertyPath<Котенок>(x => x.__PrimaryKey),
Information.ExtractPropertyPath<Котенок>(x => x.Глупость),
};
var котенокDynamicView = new View(new ViewAttribute("котенокDynamicView", котенокPropertiesNames), typeof(Котенок));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor duplicate view creation logic.

The view creation logic is duplicated across all test methods. Consider:

  1. Extract view creation to helper methods
  2. Create reusable view constants

Example refactoring:

private static class TestViews
{
    public static View GetКотенокView(bool includeMaster = false)
    {
        var properties = new[]
        {
            Information.ExtractPropertyPath<Котенок>(x => x.__PrimaryKey),
            Information.ExtractPropertyPath<Котенок>(x => x.Глупость),
        };
        
        if (includeMaster)
        {
            properties = properties.Concat(new[]
            {
                Information.ExtractPropertyPath<Котенок>(x => x.Кошка),
            }).ToArray();
        }
        
        return new View(new ViewAttribute("котенокDynamicView", properties), typeof(Котенок));
    }
}

Also applies to: 215-221

Comment on lines +272 to +278
foreach (Type type in masterLightLoadTypes)
{
if (!type.IsSubclassOf(typeof(DataObject)))
{
throw new ArgumentException("MasterLightLoad option can be set only for a DataObject.", nameof(masterLightLoadTypes));
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null check for elements in masterLightLoadTypes

There is no check for null elements within the masterLightLoadTypes collection. If a null Type is encountered, it could lead to a NullReferenceException. Consider adding a null check for each element.

Apply this diff to include the null check:

foreach (Type type in masterLightLoadTypes)
{
+    if (type is null)
+    {
+        throw new ArgumentException("Type in masterLightLoadTypes cannot be null.", nameof(masterLightLoadTypes));
+    }
    if (!type.IsSubclassOf(typeof(DataObject)))
    {
        throw new ArgumentException("MasterLightLoad option can only be set for types inheriting from DataObject.", nameof(masterLightLoadTypes));
    }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
foreach (Type type in masterLightLoadTypes)
{
if (!type.IsSubclassOf(typeof(DataObject)))
{
throw new ArgumentException("MasterLightLoad option can be set only for a DataObject.", nameof(masterLightLoadTypes));
}
}
foreach (Type type in masterLightLoadTypes)
{
if (type is null)
{
throw new ArgumentException("Type in masterLightLoadTypes cannot be null.", nameof(masterLightLoadTypes));
}
if (!type.IsSubclassOf(typeof(DataObject)))
{
throw new ArgumentException("MasterLightLoad option can be set only for a DataObject.", nameof(masterLightLoadTypes));
}
}

Comment on lines +811 to +812
lightView.Details = new DetailInView[0];

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential issue with clearing DetailsInView

In the LoadDataObject method, you're cloning the view and then clearing its details:

DetailInView[] lightViewDetails = lightView.Details;
lightView.Details = new DetailInView[0];

If lightView.Details is expected to be a modifiable collection, replacing it with a new empty array might not reflect in all parts of the code that use the original collection. Consider clearing the existing collection instead of assigning a new array.

Apply this diff to clear the collection:

- lightView.Details = new DetailInView[0];
+ lightView.Details.Clear();

Ensure that Details is a collection type that supports the Clear method.

Comment on lines +777 to +786
if (dataObjectFromCache.GetStatus(false) == ObjectStatus.UnAltered
&& dataObjectFromCache.GetLoadingState() != LoadingState.Loaded)
{
// Если объект не новый и не загружен целиком (начиная с [email protected]).
if (dataObjectFromCache.GetStatus(false) == ObjectStatus.UnAltered
&& dataObjectFromCache.GetLoadingState() != LoadingState.Loaded)
// Для обратной совместимости сравним перечень загруженных свойств и свойств в представлении.
/* Данный код срабатывает, например, если в кэше был объект, который загрузился только на уровне первичного ключа.
*
* Данный код также срабатывает в следующей ситуации: есть класс А, у него детейл Б, у которого есть наследник В.
* При загрузке объекта класса А подгрузятся его детейлы, однако они будут подгружены по представлению, которое соответствует классу Б, даже если детейлы класса В.
* Таким образом, в кэше окажутся объекты класса В, которые загружены только по свойствам Б. Раз не все свойства подгружены, то состояние LightLoaded.
* Догружать необходимо только те свойства, что ещё не загружались (потому что загруженные уже могут быть изменены).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Optimize loading state checks

In the condition checking the loading state of dataObjectFromCache:

if (dataObjectFromCache.GetStatus(false) == ObjectStatus.UnAltered
    && dataObjectFromCache.GetLoadingState() != LoadingState.Loaded)
{
    // Additional logic
}

Consider combining the status and loading state checks into a single method or property if possible, to simplify the condition and improve code readability.

Refactor the condition into a method like NeedsLoading(dataObjectFromCache) for clarity.

Comment on lines +925 to 929
private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object key, List<DataObject> dObjs, bool endObject = false)
{
if (edmEntity == null)
{
return null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure consistent null handling for edmEntity parameter

In the GetDataObjectByEdmEntity method, when edmEntity is null, the method returns null without any error or exception. Considering that edmEntity is essential for data object retrieval, it would be better to throw an ArgumentNullException to prevent silent failures and aid in debugging.

Apply this diff to enforce null checking:

                 private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object key, List<DataObject> dObjs, bool endObject = false)
                 {
-                    if (edmEntity == null)
-                    {
-                        return null;
-                    }
+                    if (edmEntity == null)
+                    {
+                        throw new ArgumentNullException(nameof(edmEntity));
+                    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object key, List<DataObject> dObjs, bool endObject = false)
{
if (edmEntity == null)
{
return null;
private DataObject GetDataObjectByEdmEntity(EdmEntityObject edmEntity, object key, List<DataObject> dObjs, bool endObject = false)
{
if (edmEntity == null)
{
throw new ArgumentNullException(nameof(edmEntity));
}

Comment on lines +899 to +915
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;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle potential NullReferenceException in GetKey method

In the GetKey method, if the key property is not found, keyProperty will be null, leading to a possible NullReferenceException when accessing keyProperty.Name at line 912. It's advisable to check if keyProperty is null and throw a meaningful exception if the key property is missing.

Apply this diff to handle the null case:

                 var keyProperty = entityProps.FirstOrDefault(prop => prop.Name == _model.KeyPropertyName);
+                if (keyProperty == null)
+                {
+                    throw new InvalidOperationException($"Key property '{_model.KeyPropertyName}' not found in the entity of type '{entityType.FullTypeName()}'.");
+                }
                 edmEntity.TryGetPropertyValue(keyProperty.Name, out key);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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;
}
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);
if (keyProperty == null)
{
throw new InvalidOperationException($"Key property '{_model.KeyPropertyName}' not found in the entity of type '{entityType.FullTypeName()}'.");
}
edmEntity.TryGetPropertyValue(keyProperty.Name, out key);
return key;
}

Copy link

sonarcloud bot commented Oct 23, 2024

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarCloud

Catch issues before they fail your Quality Gate with our IDE extension SonarLint

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants