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

Index attribute on BaseEntityTableData prevents CosmosEntityTableData with CosmosDB EF Provider #173

Open
richard-einfinity opened this issue Dec 28, 2024 · 9 comments · May be fixed by #196
Labels
Client Improvements or additions to the client code
Milestone

Comments

@richard-einfinity
Copy link
Contributor

Describe the bug

Index attribute on BaseEntityTableData prevents CosmosEntityTableData with CosmosDB EF Provider. Context throws System.InvalidOperationException. The entity type 'ModelName' has an index defined over properties 'UpdatedAt,Deleted'. The Azure Cosmos DB provider for EF Core currently does not support index definitions.

To Reproduce

Steps to reproduce the behavior:

Define Models based on CosmosEntityTableData
Add Models to Context and perform entity configuration.
Call await dbContext.Database.EnsureCreatedAsync();

Expected behavior

Database and containers to be created in CosmosDB

What platforms?

Note: Any bug or feature request that is opened for an unsupported environment will be automatically closed.

  • Server:
    • Version of dotnet being used to compile? net8
    • Library versions? CommunityToolkit.Datasync.Server.EntityFrameworkCore 8.0.4.
    • What database are you using? CosmosDB
    • Where are you running the server? Azure

Additional context

Worked around by creating a new CosmosEntityTableData base model inheriting from ITableData directly.

@richard-einfinity richard-einfinity added Bug Requires Triage This issue has not been checked by the project team. labels Dec 28, 2024
@adrianhall adrianhall added Client Improvements or additions to the client code and removed Requires Triage This issue has not been checked by the project team. labels Jan 1, 2025
@adrianhall adrianhall added this to the 8.0.5 milestone Jan 1, 2025
@adrianhall
Copy link
Collaborator

Do you have a minimal repro that I can look at?

@richard-einfinity
Copy link
Contributor Author

I'll put together a repo based on the Todo Sample. There's a few other issues that have come up which will be a problem for 9.0. I'll document them separately. But just as a quick note, In the TableController.Query.cs there's a comment mentioning calling ToListAsync but the call is actually ToList in the client evaluation function. Do these methods need to be made Async?

@richard-einfinity
Copy link
Contributor Author

@adrianhall apologies this issue is related to EF9. EF8 ignores index definitions as their not mapped where EF9 throws if an index is defined.

Maybe bump to 9.0

@adrianhall adrianhall modified the milestones: 8.0.5, 9.0.0 Jan 6, 2025
@adrianhall adrianhall removed the Bug label Jan 7, 2025
@adrianhall adrianhall mentioned this issue Jan 10, 2025
@adrianhall
Copy link
Collaborator

I'm not sure I can continue to support the Cosmos database.

The repository pattern relies on the fact that we can get an IQueryable for the dataset. In fact, this is a requirement of the OData library. When we do query.ApplyTo(), it executes .ToList() underneath. Without this, we don't have a viable way of supporting query semantics within the service without re-writing OData AND converting all the other repository types (except for EF Core). Basically, we would become an EF Core only provider.

Will talk to the person who owns EF Core Cosmos driver on this - it's on hold right now. But this is a seriously large breaking change that has obviously not been thought through.

@richard-einfinity
Copy link
Contributor Author

Hi Adrian,

Did you see the code sample I included on #184? Delaying applying PageSize and $select to later avoids the sync call. And allows you to call the async on the IQueryable.

Retrieving the IQueryable seems to be fine. When $select is applied in the current implementation is where it all seems to go wrong.

For now I've created a cosmos specific repository works well and suits my needs.

If we can get both the calls in the query action to async I think it would go a long way.

EF9 will automatically recognise Partition keys defined on the IQueryable so no additional work needs to be done there as the views can be applied in the IAccessControlProvider.

Could the index attribute be moved further up to the other TableData subclasses?

I'll publish some code up to my fork.

@adrianhall
Copy link
Collaborator

I did - I’m still working through the details, and working with both the OData library engineers and EF Core team. We’ll get a suitable solution.

@adrianhall
Copy link
Collaborator

SO - got an answer, and I've got a solution.

  1. I'm adding two methods to the IRepository<T> interface. These are optional with default implementations:
    ValueTask<int> CountAsync(IQueryable query, CancellationToken cancellationToken = default)
        => ValueTask.FromResult(query.Cast<object>().Count());

    ValueTask<List<object>> ToListAsync(IQueryable query, CancellationToken cancellationToken = default)
        => ValueTask.FromResult(query.Cast<object>().ToList());

This means if you have an existing repository implementation, they will keep on working as before. However, in the EF Core implementation:

    /// <inheritdoc />
    /// <remarks>
    /// The entity framework core edition of this method uses the async method.
    /// </remarks>
    public virtual async ValueTask<int> CountAsync(IQueryable query, CancellationToken cancellationToken = default)
    {
        return await EntityFrameworkQueryableExtensions.CountAsync(query.Cast<object>(), cancellationToken).ConfigureAwait(false);
    }

    /// <inheritdoc />
    /// <remarks>
    /// The entity framework core edition of this method uses the async method.
    /// </remarks>
    public virtual async ValueTask<List<object>> ToListAsync(IQueryable query, CancellationToken cancellationToken = default)
    {
        return await EntityFrameworkQueryableExtensions.ToListAsync(query.Cast<object>(), cancellationToken).ConfigureAwait(false);
    }

They are converted automatically to the async versions. So - theory says you don't have to do anything for EF Core based repositories. However, they can be overridden so if you need to do something special, the facilities are there. For example, you could use the FeedIterator from the Cosmos SDK if you wanted.

I have not re-organized the table controller per #184 - I looked at it and did some side-by-sides. The performance improvements were marginal on most tests, and I wanted to minimize the changes to the controller.

Tests are running now (the Cosmos tests are really slow because there is no good EF Core way of clearing a table except deleting each element individually) so will let you know when I've got some results.

adrianhall added a commit to adrianhall/CommunityToolkit-Datasync that referenced this issue Jan 13, 2025
adrianhall added a commit to adrianhall/CommunityToolkit-Datasync that referenced this issue Jan 13, 2025
@adrianhall
Copy link
Collaborator

Just noting down where I am right now:

  • Cosmos driver works just fine.
  • I have an unrelated error in the Azure SQL and PgSQL database drivers where the UpdatedAt field is not being read properly, resulting in the UpdatedAt field returned as "before" the start of the test. This is a test harness problem and not a problem with the code. (The actual data is correct - I just have a caching problem).

I'm going to correct the second issue before I call this done and issue the PR; however, feel free to download my branch and test out the code yourself.

adrianhall added a commit to adrianhall/CommunityToolkit-Datasync that referenced this issue Jan 13, 2025
@adrianhall
Copy link
Collaborator

... and it's fixed. It came down to Azure SQL / PgSQL microsecond resolution vs. server millisecond resolution, and comparing "ticks" instead of the actual date/time offset. PR is being submitted now and will sit around until Friday to allow others to comment on it.

@richard-einfinity - would explicitly love your review on the PR.

adrianhall added a commit to adrianhall/CommunityToolkit-Datasync that referenced this issue Jan 14, 2025
adrianhall added a commit to adrianhall/CommunityToolkit-Datasync that referenced this issue Jan 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Client Improvements or additions to the client code
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants