C# 实体框架核心中的强类型Ids

C# 实体框架核心中的强类型Ids,c#,entity-framework,.net-core,entity-framework-core,domain-driven-design,C#,Entity Framework,.net Core,Entity Framework Core,Domain Driven Design,我试图创建一个强类型的Id类,它现在在内部保存“long”。执行情况如下。 我在实体中使用它的问题是,实体框架给我一个消息,说明属性Id已经映射到它了。请参见下面我的IEntityTypeConfiguration 注意:我的目标不是严格的DDD实现。所以在评论或回答时请记住这一点。类型化的id背后的整个id是为进入项目的开发人员准备的,他们是强类型的,在他们的所有实体中使用id,当然被翻译成long(或BIGINT)-但是其他人很清楚 在类和配置下面,这不起作用。 回购协议可在 Idclas

我试图创建一个强类型的
Id
类,它现在在内部保存“long”。执行情况如下。 我在实体中使用它的问题是,实体框架给我一个消息,说明属性Id已经映射到它了。请参见下面我的
IEntityTypeConfiguration

注意:我的目标不是严格的DDD实现。所以在评论或回答时请记住这一点。类型化的
id
背后的整个id是为进入项目的开发人员准备的,他们是强类型的,在他们的所有实体中使用id,当然被翻译成
long
(或
BIGINT
)-但是其他人很清楚

在类和配置下面,这不起作用。 回购协议可在

  • Id
    class at(现在已注释掉):
  • 实体
    值对象
    类(其中对于
    实体
    而言,
    Id
    属性的类型为
    Id
    .cs(如上所述):
  • 配置位于:
Id
类实现
(现在标记为过时,因为在找到解决方案之前我放弃了这个想法)

实体
基类(当我还在使用Id时,所以当它没有被标记为过时时)

我的目标不是要有一个严格的DDD实现。所以在评论或回答时请记住这一点。类型化id后面的整个id是给来项目的开发人员的,他们是强类型的,在他们的所有实体中使用id

那么为什么不添加一个类型别名呢:

using Id = System.Int64;

我认为你运气不好。你的用例非常罕见。EF Core 3.1.1仍在努力将SQL添加到数据库中,除了最基本的用例外,数据库中没有任何损坏


所以,你必须写一些经过LINQ树的东西,这可能是一个巨大的工作量,如果你在EF Core上偶然发现bug,你会很高兴在你的罚单中解释它。

所以在搜索了很长一段时间后,试图得到更多的答案,我找到了,就在这里。感谢Andrew Lock

EF Core中的强类型ID:使用强类型实体ID避免原始困扰-第4部分:

TL;安德鲁博士/总结
在这篇文章中,我介绍了一种在EF核心实体中使用强类型ID的解决方案,方法是使用值转换器和自定义的IValueConverterSelector。EF核心框架中的基值转换器Selector用于注册基本类型之间的所有内置值转换。通过从该类派生,我们可以将强类型ID添加到转换到此列表,并在整个EF核心查询过程中实现无缝转换

当然,我喜欢这个想法。但每次您都会使用“Id”在一个.cs文件中,您是否必须确保将这个using语句放在上面?当传递一个类时,您不必这样做?而且我将丢失其他基类功能,例如
Id.Empty
..,或者必须在扩展方法中实现它…我喜欢这个想法,thx供您考虑。如果没有其他解决方案出现,我会满足于此,因为这清楚地表明了目的。我同意用例是罕见的,但它背后的想法并不完全愚蠢,我希望…?如果是,请让我知道。如果它愚蠢(到目前为止还不确信,因为强类型ID在域中很容易编程),或者如果我不能很快找到答案,我可能会使用下面David Browne-Micrososft建议的别名().到目前为止,其他用例、集合和EF Core中的隐藏字段都很好,没有bug,所以我觉得这很奇怪,因为我对该产品有很好的体验。这本身并不愚蠢,但很少有enoug,因为我从未见过支持它的orm,EfCore太差了,所以我现在正在努力删除它并将其移回to Ef(非核心),因为我需要发货。对我来说,EfCore 2.2工作得更好-3.1是100%不可用的,因为我使用的任何投影都会导致糟糕的sql或“我们不再评估客户端”即使-2.2在服务器上进行了完美的评估。所以,我不希望他们在核心功能被破坏的情况下花时间在这样的事情上。更详细地说,对于3.1被破坏的情况,EfCore团队决定不再评估客户端是有原因的,他们甚至在2.2中发出警告,让您为即将到来的更改做好准备或者说,我看不出这件事有什么问题。至于其他我不能评论的东西,我也看到了问题,但我能够在没有任何性能成本的情况下解决它们。另一方面,在我为生产做的最后3个项目中,其中2个是基于整洁的,一个基于环境足迹的……也许我应该为这一个选择整洁的路线,但却击败了pu新开发人员轻松入门的用途:-)。。。我们拭目以待。问题是什么是服务器端评估的定义。他们甚至吹嘘一些非常简单的东西,这些东西可以完美地工作。删除功能,直到它被使用。我们只需删除EfCore并返回EF。EF+第三方进行全局过滤=工作。dapper的问题是,我允许每个复杂的用户决定LINQ-我必须将它从bo转换为服务器端查询。在EF2.2工作,现在完全瘫痪了。好吧,我现在读到这个。。。我明白你的意思了你在用什么第三方库?我不明白你的意思,你能把你所说的关于整洁的话改一改吗。对我来说,它是有效的,但它是一个低调的项目,团队中只有两个开发人员——当然,还有很多手动模板要编写,以使它高效地工作。。。
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);
        }
    }
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;
    }
}
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);
    }
}
using Id = System.Int64;