Я настроил модель 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 ), _ ) );
}
}
}