Entity Framework Core и CosmosDb - проблема при запросе свойства, имеющего тип enum и сериализованного в виде строки - PullRequest
0 голосов
/ 24 марта 2020

Я настроил модель EF Core 3.1.2 для сериализации перечислений в виде строк вместо целых чисел. Теперь запросы не могут быть оценены на сервере. Код ниже демонстрирует проблему. Есть ли способ сериализации перечислений в виде строк и оценки запросов на сервере?

Приведенный ниже код демонстрирует проблему.

// Referenced NuGet packages:
// - Microsoft.EntityFrameworkCore 3.1.2
// - Microsoft.EntityFrameworkCore.Cosmos 3.1.2
// - Microsoft.Extensions.DependencyInjection 3.1.2

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace CosmosDbDemo
{
  #region CosmosDb parameters
  // Define here your CosmosDb database connection
  class CosmosDbParameters
  {
    public const string AccountEndPoint = "{YOUR-ENDPOINT-HERE}";
    public const string AccountKey = "{YOUR-ACCOUNT-KEY-HERE}";
    public const string DbName = "DemoIssues";
    public const string DbContainer = "Offers";
  }
#endregion


class Program
{
    static async Task Main( string[] args )
    {
        // Configure services
        var serviceProvider =
            new ServiceCollection()
            .AddDbContext<OfferContext>( 
                options => options.UseCosmos(  CosmosDbParameters.AccountEndPoint, CosmosDbParameters.AccountKey, CosmosDbParameters.DbName ) )
            .BuildServiceProvider();

        using var ctx = serviceProvider.GetService<OfferContext>();

        // Ensure database has been properly created.
        ctx.Database.EnsureCreated();

        // Insert some items. A future query will try to retrieve only some of them.
        await AddItems( ctx );

        // Query for some items. Reproduces the issue with enums
        // serialized as text.
        await QueryUsingEFWithServerEvaluation( ctx );

        // Query for items, but first pull all items to the client
        // and then execute a filtering operation. This one works,
        // but it's not acceptable.
        await QueryUsingEFWithClientEvaluation( ctx );
    }

    private static async Task QueryUsingEFWithClientEvaluation( OfferContext ctx )
    {
        try
        {
            var offers = await ctx
                            .Offers
                            .ToListAsync();
            offers = offers.Where( _ => _.Status == OfferStatus.Created ).ToList();

            Console.WriteLine( $"With client evaluation: {offers.Count} offers found" );
        }
        catch( Exception ex )
        {
            Console.Write( ex );
        }
    }

    private static async Task QueryUsingEFWithServerEvaluation( OfferContext ctx )
    {
        try
        {
            // The call below fails with exception
            // "Invalid cast from 'System.Int32' to 'CosmosDbDemo.Model.OfferStatus'"
            var offers = await ctx
                            .Offers
                            .Where( _ => _.Status == OfferStatus.Created  )
                            .ToListAsync();

            Console.WriteLine( $"With server evaluation: {offers.Count} offers found" );
        }
        catch( Exception ex )
        {
            Console.Write( ex );
        }
    }

    private static async Task AddItems( OfferContext ctx )
    {
        var offer = BuildOffer( "Acme Offer 1", 14 );
        ctx.Add( offer );
        offer = BuildOffer( "Acme Offer 2", 30 );
        ctx.Add( offer );
        offer = BuildOffer( "Coyote Ugly Super Offer 1", 25 );
        ctx.Add( offer );
        offer = BuildOffer( "Sloth Supercars Offer 14", 10 );
        ctx.Add( offer );
        await ctx.SaveChangesAsync();
    }

    private static Offer BuildOffer( string name, int valabilityInDays )
    {
        var result = new Offer
        {
            Id = Guid.NewGuid(),
            Name = name,
            ExpirationDateUtc = DateTime.UtcNow.Date.AddDays( valabilityInDays ),
            Status = OfferStatus.Created
        };

        return result;
    }
}

class Offer
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public DateTime ExpirationDateUtc { get; set; }
    public OfferStatus Status { get; set; }
}

enum OfferStatus
{
    Created = 0,
    Approved = 1,
    Accepted = 2,
    Declined = 3
}

class OfferContext : DbContext
{
    public OfferContext( DbContextOptions<OfferContext> options ) : base( options )
    {

    }

    public DbSet<Offer> Offers { get; set; }


    protected override void OnModelCreating( ModelBuilder modelBuilder )
    {
        modelBuilder.HasDefaultContainer( CosmosDbParameters.DbContainer );
        modelBuilder.Entity<Offer>().HasPartitionKey( _ => _.Id );
        modelBuilder.Entity<Offer>().Property( _ => _.Id )
                .HasConversion( _ => _.ToString( "B" ), _ => Guid.Parse( _ ) );
        modelBuilder.Entity<Offer>().Property( _ => _.Status )
                .HasConversion( _ => _.ToString(), _ => (OfferStatus)Enum.Parse( typeof( OfferStatus ), _ ) );
    }
}

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...