Id Sangat Diketik dalam Entity Framework Core

12

Saya mencoba untuk memiliki Idkelas yang sangat diketik , yang sekarang memegang 'panjang' secara internal. Implementasi di bawah ini. Masalah yang saya alami saat menggunakan ini di entitas saya adalah bahwa Entity Framework memberi saya pesan bahwa ID properti sudah dipetakan di atasnya. Lihat saya di IEntityTypeConfigurationbawah ini.

Catatan: Saya tidak bermaksud memiliki implementasi DDD yang kaku. Jadi harap ingat hal ini saat berkomentar atau menjawab . Seluruh id di belakang yang diketik Idadalah untuk pengembang yang datang ke proyek mereka diketik kuat untuk menggunakan ID di semua entitas mereka, tentu saja diterjemahkan ke long(atau BIGINT) - tetapi jelas kemudian untuk orang lain.

Di bawah kelas & konfigurasi, yang tidak berfungsi. Repo dapat ditemukan di https://github.com/KodeFoxx/Kf.CleanArchitectureTemplate.NetCore31 ,

Idimplementasi kelas (ditandai sudah usang sekarang, karena saya meninggalkan ide sampai saya menemukan solusi untuk ini)

namespace Kf.CANetCore31.DomainDrivenDesign
{
    [DebuggerDisplay("{DebuggerDisplayString,nq}")]
    [Obsolete]
    public sealed class Id : ValueObject
    {
        public static implicit operator Id(long value)
            => new Id(value);
        public static implicit operator long(Id value)
            => value.Value;
        public static implicit operator Id(ulong value)
            => new Id((long)value);
        public static implicit operator ulong(Id value)
            => (ulong)value.Value;
        public static implicit operator Id(int value)
            => new Id(value);


        public static Id Empty
            => new Id();

        public static Id Create(long value)
            => new Id(value);

        private Id(long id)
            => Value = id;
        private Id()
            : this(0)
        { }

        public long Value { get; }

        public override string DebuggerDisplayString
            => this.CreateDebugString(x => x.Value);

        public override string ToString()
            => DebuggerDisplayString;

        protected override IEnumerable<object> EquatableValues
            => new object[] { Value };
    }
}

EntityTypeConfigurationSaya menggunakan ketika Id tidak ditandai usang untuk entitasPerson Sayangnya, ketika dari tipe Id, EfCore tidak ingin memetakannya ... ketika tipe lama itu tidak masalah ... Tipe yang dimiliki lainnya, seperti yang Anda lihat (dengan Name) bekerja dengan baik.

public sealed class PersonEntityTypeConfiguration
        : IEntityTypeConfiguration<Person>
    {
        public void Configure(EntityTypeBuilder<Person> builder)
        {
            // this would be wrapped in either a base class or an extenion method on
            // EntityTypeBuilder<TEntity> where TEntity : Entity
            // to not repeated the code over each EntityTypeConfiguration
            // but expanded here for clarity
            builder
                .HasKey(e => e.Id);
            builder
                .OwnsOne(
                e => e.Id,
                id => {
                   id.Property(e => e.Id)
                     .HasColumnName("firstName")
                     .UseIdentityColumn(1, 1)
                     .HasColumnType(SqlServerColumnTypes.Int64_BIGINT);
                }

            builder.OwnsOne(
                e => e.Name,
                name =>
                {
                    name.Property(p => p.FirstName)
                        .HasColumnName("firstName")
                        .HasMaxLength(150);
                    name.Property(p => p.LastName)
                        .HasColumnName("lastName")
                        .HasMaxLength(150);
                }
            );

            builder.Ignore(e => e.Number);
        }
    }

Entity kelas dasar (ketika saya masih menggunakan ID, jadi ketika itu tidak ditandai usang)

namespace Kf.CANetCore31.DomainDrivenDesign
{
    /// <summary>
    /// Defines an entity.
    /// </summary>
    [DebuggerDisplay("{DebuggerDisplayString,nq}")]
    public abstract class Entity
        : IDebuggerDisplayString,
          IEquatable<Entity>
    {
        public static bool operator ==(Entity a, Entity b)
        {
            if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
                return true;

            if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
                return false;

            return a.Equals(b);
        }

        public static bool operator !=(Entity a, Entity b)
            => !(a == b);

        protected Entity(Id id)
            => Id = id;

        public Id Id { get; }

        public override bool Equals(object @object)
        {
            if (@object == null) return false;
            if (@object is Entity entity) return Equals(entity);
            return false;
        }

        public bool Equals(Entity other)
        {
            if (other == null) return false;
            if (ReferenceEquals(this, other)) return true;
            if (GetType() != other.GetType()) return false;
            return Id == other.Id;
        }

        public override int GetHashCode()
            => $"{GetType()}{Id}".GetHashCode();

        public virtual string DebuggerDisplayString
            => this.CreateDebugString(x => x.Id);

        public override string ToString()
            => DebuggerDisplayString;
    }
}

Person(domain dan referensi ke ValueObjects lain dapat ditemukan di https://github.com/KodeFoxx/Kf.CleanArchitectureTemplate.NetCore31/tree/master/Core/Domain/Kf.CANetCore31.Core.Domain/People )

namespace Kf.CANetCore31.Core.Domain.People
{
    [DebuggerDisplay("{DebuggerDisplayString,nq}")]
    public sealed class Person : Entity
    {
        public static Person Empty
            => new Person();

        public static Person Create(Name name)
            => new Person(name);

        public static Person Create(Id id, Name name)
            => new Person(id, name);

        private Person(Id id, Name name)
            : base(id)
            => Name = name;
        private Person(Name name)
            : this(Id.Empty, name)
        { }
        private Person()
            : this(Name.Empty)
        { }

        public Number Number
            => Number.For(this);
        public Name Name { get; }

        public override string DebuggerDisplayString
            => this.CreateDebugString(x => x.Number.Value, x => x.Name);
    }
}
Yves Schelpe
sumber

Jawaban:

3

Saya tidak bermaksud memiliki implementasi DDD yang kaku. Jadi harap ingat hal ini saat berkomentar atau menjawab. Seluruh id di belakang Id yang diketik adalah untuk pengembang yang datang ke proyek mereka sangat diketik untuk menggunakan Id di semua entitas mereka

Lalu mengapa tidak menambahkan saja jenis alias:

using Id = System.Int64;
David Browne - Microsoft
sumber
Tentu, saya suka idenya. Tetapi setiap kali Anda akan menggunakan "Id" dalam file .cs, tidakkah Anda harus memastikan untuk menempatkan pernyataan menggunakan ini di atas - sementara dengan kelas yang diedarkan, orang tidak harus? Juga saya akan kehilangan fungsionalitas kelas dasar lainnya seperti Id.Empty..., atau harus mengimplementasikannya jika tidak dalam metode ekstensi kemudian ... Saya suka idenya, terima kasih untuk berpikir bersama. Jika tidak ada solusi lain yang muncul saya akan puas dengan ini, karena ini jelas menyatakan niat.
Yves Schelpe
3

Jadi setelah lama mencari, dan mencoba mendapatkan lebih banyak jawaban, saya menemukannya, ini dia. Terima kasih kepada Andrew Lock.

ID yang diketik dalam EF Core: Menggunakan ID entitas yang diketik dengan kuat untuk menghindari obsesi primitif - Bagian 4 : https://andrewlock.net/strongly-typed-ids-in-ef-core-using-strongly-typed-entity- id-untuk-menghindari-primitif-obsesi-bagian-4 /

TL; DR / Ringkasan Andrew Dalam postingan ini saya menjelaskan solusi untuk menggunakan ID yang sangat diketik dalam entitas EF Core Anda dengan menggunakan pengonversi nilai dan pemilih nilai IV kustomConverterSelektor. ValueConverterSelector dasar dalam kerangka Core EF digunakan untuk mendaftarkan semua konversi nilai bawaan antara tipe primitif. Dengan berasal dari kelas ini, kita dapat menambahkan konverter ID yang sangat diketik ke dalam daftar ini, dan mendapatkan konversi tanpa batas di seluruh kueri EF Core kami

Yves Schelpe
sumber
2

Saya pikir Anda kurang beruntung. Kasing penggunaan Anda sangat jarang. Dan EF Core 3.1.1 masih berjuang dengan menempatkan SQL ke dalam basis data yang tidak rusak apa pun kecuali sebagian besar kasus dasar.

Jadi, Anda harus menulis sesuatu yang melewati pohon LINQ dan ini kemungkinan adalah pekerjaan yang luar biasa, dan jika Anda menemukan bug di EF Core - yang akan Anda - bersenang-senang menjelaskannya di tiket Anda.

TomTom
sumber
Saya setuju penggunaannya jarang, tetapi ide di baliknya tidak sepenuhnya bodoh, saya mungkin berharap ...? Jika demikian, beri tahu saya. Jika itu bodoh (yakin sejauh ini tidak, karena id yang diketik sangat mudah diprogram di domain), atau jika saya tidak menemukan jawaban dengan cepat, saya mungkin menggunakan alias seperti yang disarankan oleh David Browne - Micrososft di bawah ( stackoverflow .com / a / 60155275/1155847 ). Sejauh ini baik pada kasus penggunaan lainnya, dan koleksi dan bidang tersembunyi di EF Core, tidak ada bug, jadi saya pikir itu aneh, karena kalau tidak, saya memiliki pengalaman yang baik dengan produk ini.
Yves Schelpe
Ini tidak bodoh, tetapi jarang diketahui bahwa TIDAK ada orm yang pernah saya lihat mendukungnya dan EfCore sangat buruk sehingga saat ini saya sedang bekerja untuk menghapusnya dan kembali ke Ef (bukan inti) karena saya perlu mengirim. Bagi saya EfCore 2.2 bekerja lebih baik - 3.1 adalah 100% tidak dapat digunakan karena setiap proyeksi yang saya gunakan menghasilkan hasil sql yang buruk atau "kami tidak mengevaluasi sisi klien lagi" walaupun - 2.2 mengevaluasi dengan sempurna di server. Jadi, saya tidak berharap mereka menghabiskan waktu untuk hal-hal seperti itu - sementara fungsi inti mereka rusak. github.com/dotnet/efcore/issues/19830#issuecomment-584234667 untuk lebih jelasnya
TomTom
EfCore 3.1 rusak, ada alasan mengapa tim EfCore memutuskan untuk tidak mengevaluasi sisi klien lagi, mereka bahkan mengeluarkan peringatan tentang hal itu di 2.2 untuk mempersiapkan Anda untuk perubahan yang akan datang. Sedangkan untuk itu, saya tidak melihat bahwa benda itu rusak. Adapun hal-hal lain yang tidak bisa saya komentari, saya pernah melihat masalah, tetapi bisa menyelesaikannya tanpa biaya. Di sisi lain, pada 3 proyek terakhir yang saya lakukan untuk produksi 2 di antaranya berbasis Dapper, satu berbasis Ef ... Mungkin saya harus bertujuan untuk pergi rute necis untuk yang satu ini, tetapi mengalahkan tujuan masuknya yang mudah untuk pengembang baru :-)... Kita lihat saja nanti.
Yves Schelpe
Masalahnya adalah definisi dari evaluasi sisi server. Mereka bahkan meniup hal-hal yang sangat sederhana yang bekerja dengan sempurna. Fungsionalitas ripped out sampai itu useles. Kami hanya menghapus EfCore dan kembali ke EF. EF + pihak ke-3 untuk penyaringan global = bekerja. Masalah dengan dapper adalah bahwa saya mengizinkan setiap pengguna yang kompleks memutuskan LINQ - Saya HARUS menerjemahkannya dari bo ke dalam permintaan sisi server. Bekerja di Ef 2.2, benar-benar borking sekarang.
TomTom
Ok, sekarang saya baca ini github.com/dotnet/efcore/issues/19679#issuecomment-583650245 ... Saya mengerti maksud Anda lib pihak ketiga apa yang Anda gunakan? Bisakah Anda ulangi apa yang Anda katakan tentang Dapper, karena saya tidak mengerti apa yang Anda maksud. Bagi saya itu berhasil, tetapi itu adalah proyek-proyek yang low key dengan hanya 2 devs di tim - dan banyak boilerplate manual untuk menulis agar dapat bekerja secara efisien tentu saja ...
Yves Schelpe