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

Feature request: add support of LINQ expression for value of primitive types #31445

Closed
amyboose opened this issue Aug 11, 2023 · 1 comment
Closed

Comments

@amyboose
Copy link

amyboose commented Aug 11, 2023

What problem are you trying to solve?

I use primitive types in my project. But primitive types doesn't contains all methods which exists in internal type.

public readonly struct SKU
{
    public SKU(string value)
    {
        Value = value;
    }

    public string Value { get; }
}

Type String contain method StartsWith, but SKU as primitive type doesn't contain it.
In EF Core it leads me to use explicit converter in every place where I want to use some specific method.

My code:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace PrimitiveTypes;
public class Program
{
    public static async Task Main(params string[] args)
    {
        MyContext context = new();
        context.Database.EnsureDeleted();
        context.Database.EnsureCreated();

        var sku = new SKU("myValue");

        //example 1
        //it works
        Product? result1 = await context
            .Products
            .FirstOrDefaultAsync(x => x.SKU == sku);

        //example 2
        //it also works
        Product? result2 = await context
            .Products
            .FirstOrDefaultAsync(x => ((string)x.SKU).StartsWith("R"));

        //example 3
        //throw an Exception
        Product? result3 = await context
            .Products
            .FirstOrDefaultAsync(x => x.SKU.Value.StartsWith("R"));
    }

}

public class Product
{
    public int Id { get; set; }
    public SKU SKU { get; set; }
}

public readonly struct SKU
{
    public SKU(string value)
    {
        Value = value;
    }

    public string Value { get; }
    public bool Equals(SKU other) => this.Value.Equals(other.Value);
    public override bool Equals(object? obj)
    {
        if (obj is null)
            return false;

        return obj is SKU other && Equals(other);
    }

    public override int GetHashCode() => Value.GetHashCode();

    public override string ToString() => Value.ToString();
    public static bool operator ==(SKU a, SKU b) => a.Equals(b);
    public static bool operator !=(SKU a, SKU b) => !(a == b);
    public int CompareTo(SKU other) => Value.CompareTo(other.Value);

    public static explicit operator string(SKU sku) => sku.Value;
}

public class SKUConverter : ValueConverter<SKU, string>
{
    public SKUConverter()
        : base(x => x.Value, x => new SKU(x))
    {

    }
}

public class MyContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseNpgsql(@"Host=localhost;Port=7435;Database=testdb;Username=admin;Password=testpass")
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>(builder =>
        {
            builder
                .ToTable("Products");

            builder
                .Property(p => p.Id)
                .HasColumnName("Id");

            builder
                .Property(p => p.SKU)
                .HasConversion<SKUConverter>();
        });
    }
}

I want to use expressions as in the third example. But it doesn't work and I know about it. Right now the solution is to use it like in the 2nd example.
The main problem is lack of internal type's methods using primitive types. Using Value is a lot more convenient way than using explicit operator.

Describe the solution you'd like

Primitive types is constructed by ValueConverter. Constructor of ValueConverter contains a LambdaExpression. The LambdaExpression can be deconstructed to expression tree where primitive type looks like x.Value or similar.
And the expression from database query also contains the same syntax in my third example.
Сan you consider adding this translation of LINQ expression?

@ajcvickers
Copy link
Member

Duplicate of #10434

@ajcvickers ajcvickers marked this as a duplicate of #10434 Sep 5, 2023
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Sep 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants