web-dev-qa-db-ja.com

Entity Framework Coreで強く型付けされたID

強く型付けされたIdクラスを作成しようとしていますが、これは内部で「long」を保持しています。以下の実装。私のエンティティでこれを使用している問題は、Entity Frameworkから、プロパティIdが既にあるというメッセージが表示されることですそれにマッピングされました。以下のIEntityTypeConfigurationをご覧ください。

注:私は厳密なDDD実装を目指していません。 コメントや回答をするときはこれを覚えておいてください。型指定されたIdの背後にあるID全体は、すべてのエンティティでIdを使用するために強く型付けされているプロジェクトにアクセスする開発者向けです。もちろん、long(またはBIGINT)に変換されますが、他の場合は明らかです。

機能しないクラスと設定の下。リポジトリは https://github.com/KodeFoxx/Kf.CleanArchitectureTemplate.NetCore31 にあります。

Idクラスの実装(このソリューションは、解決策が見つかるまで放棄されたため、廃止されました)

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 };
    }
}

EntityTypeConfiguration IdがエンティティPersonの廃止としてマークされていないときに使用していましたが、残念ながらId型の場合、EfCoreはそれをマップしたくありませんでした...タイプlongの場合は問題ありませんでした...他の所有されたタイプは(Nameを使用して)正常に機能します。

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基本クラス(まだIdを使用していたため、廃止としてマークされていなかった場合)

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(ドメインと他のValueObjectsへの参照は https://github.com/KodeFoxx/Kf.CleanArchitectureTemplate。 NetCore31/tree/master/Source/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);
    }
}
12
Yves Schelpe

運が悪いと思います。ユースケースは非常にまれです。そして、EF Core 3.1.1は、最も基本的なケースを除いて、何も壊れていないSQLをデータベースに配置することにまだ苦労しています。

したがって、LINQツリーを通過する何かを記述する必要があります。これはおそらく膨大な量の作業であり、EF Coreのバグに遭遇した場合-チケットでそれを説明するのが楽しいでしょう。

2
TomTom