Staram się mieć silnie napisaną Idklasę, która teraz wewnętrznie utrzymuje „długi”. Realizacja poniżej. Problem polegający na używaniu tego w moich jednostkach polega na tym, że Entity Framework daje mi komunikat, że identyfikator właściwości jest już na nim zmapowany. Zobacz moje IEntityTypeConfigurationponiżej.
Uwaga: Nie zamierzam mieć sztywnej implementacji DDD. Więc proszę o tym pamiętać, gdy komentując odebraniem . Cały identyfikator wpisany Idjest dla programistów przybywających do projektu, którzy są mocno wpisani, aby używać Id we wszystkich swoich obiektach, oczywiście przetłumaczone na long(lub BIGINT) - ale jest to jasne dla innych.
Poniżej klasy i konfiguracji, która nie działa. Repozytorium można znaleźć na stronie https://github.com/KodeFoxx/Kf.CleanArchitectureTemplate.NetCore31 ,
Idklasa at (skomentowano teraz): https://github.com/KodeFoxx/Kf.CleanArchitectureTemplate.NetCore31/blob/master/Source/Common/Kf.CANetCore31/DomainDrivenDesign/Id.csEntityiValueObjectklasy (gdzieEntitywłaściwośćIdbyła typuId.cs (powyżej): https://github.com/KodeFoxx/Kf.CleanArchitectureTemplate.NetCore31/tree/master/Source/Common/Kf.CANetCore31/DomainDrivenDesign- Konfiguracje: https://github.com/KodeFoxx/Kf.CleanArchitectureTemplate.NetCore31/tree/master/Source/Infrastructure/Persistence/Kf.CANetCore31.Infrastructure.Persistence.Ef/EntityTypeConfigurations
Idimplementacja klasy (teraz oznaczona jako przestarzała, ponieważ porzuciłem pomysł, dopóki nie znalazłem rozwiązania)
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 };
}
}
EntityTypeConfigurationPersonUżywałem, gdy nie oznaczono identyfikatora jako przestarzały dla bytu Niestety jednak, gdy typu Id, EfCore nie chciał go zmapować ... kiedy typu był długi, nie było problemu ... Inne posiadane typy, jak widzisz (z Name) dobrze pracować.
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 klasa podstawowa (kiedy wciąż korzystałem z Id, więc kiedy nie było zaznaczone jako przestarzałe)
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(domenę i odniesienia do innych ValueObjects można znaleźć na stronie 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);
}
}
Id.Empty..., lub musiałbym ją zaimplementować w metodzie rozszerzenia, a następnie ... Podoba mi się pomysł, dzięki za myślenie dalej. Jeśli nie znajdzie się żadne inne rozwiązanie, zadowoliłbym się tym, ponieważ wyraźnie stwierdza to zamiar.