Efcore implementation value object of DDD

Time:2022-4-16

Efcore implementation value object of DDD

Region

public record Region
{
    public long Id { get; init; }
    public MultilingualString Name { get; init; }
    public Area Area { get; init; }
    public RegionLevel Level { get; private set; }
    public long?  Population { get; private set; } // population
    public Geo Location { get; init; }
    private Region() { }
    public Region(MultilingualString name, Area area, Geo location,
        RegionLevel level)
    {
        this.Name = name;
        this.Area = area;
        this.Location = location;
        this.Level = level;
    }

    public void ChangePopulation(long value)
    {
        this.Population = value;
    }
    public void ChangeLevel(RegionLevel value)
    {
        this.Level = value;
    }

}

//Multilingual
public record MultilingualString(string Chinese, string? English);

//Area size
public record Area(double Value, AreaType Unit);
public enum AreaType
{
    SquareKm, // m2
    Hectare, // ha
    CnMu     //
}

//City level
public enum RegionLevel
{
    Province, // Province
    City, // City
    Country, // Country
    Town // town
}

//Longitude and latitude
public record Geo
{
    public double Longitude { get; init; }
    public double Latitude { get; init; }
    public Geo(double longitude, double latitude)
    {
        if (longitude < -180 || longitude > 180)
        {
            throw new ArgumentException("longitude invalid");
        }
        if (latitude < -90 || latitude > 90)
        {
            throw new ArgumentException("longitude invalid");
        }
        this.Longitude = longitude;
        this.Latitude = latitude;
    }
}

RegionConfig

class RegionConfig : IEntityTypeConfiguration
{
    public void Configure(EntityTypeBuilder builder)
    {
        builder.ToTable("T_Cities");
        builder.OwnsOne(c => c.Area, nb =>
        {
            nb.Property(e => e.Value)
            .HasColumnName("AreaValue")
            . hascomment ("area size");
            nb.Property(e => e.Unit)
            .HasColumnName("AreaUnit")
            .HasMaxLength(20).IsUnicode(false)
            .hasconversion() Hascomment ("unit (m2 / HA)");
        });

        builder. Property(e => e.Population). Hascomment ("population");

        builder.OwnsOne(c => c.Location, nb =>
        {
            nb.Property(e => e.Longitude)
            .HasColumnName("Longitude")
            . hascomment ("longitude");
            nb.Property(e => e.Latitude)
            .HasColumnName("Latitude")
            . hascomment ("dimension");
        });

        builder.Property(c => c.Level).HasMaxLength(20)
            .IsUnicode(false).HasConversion()
            . hascomment ("city level (country / Province / city / town)");

        builder.OwnsOne(c => c.Name, nb =>
        {
            nb.Property(e => e.English).HasMaxLength(20).IsUnicode(false)            
           .HasColumnName("NameEnglish")
            . hascomment ("English");
            nb.Property(e => e.Chinese).HasMaxLength(20).IsUnicode(true)
            .HasColumnName("NameChinese")
            . hascomment ("Chinese");
        });

    }
}

(1) “Owned entities”: configured using methods such as ownsone in fluent API.

image-20220319153933559

(2) In EF core, the attribute of an entity can be defined as an enumeration type. By default, the attribute of an enumeration type is saved as an integer type in the database. In EF core, hasconversion can be used in fluent API() configure the value of enumeration type to be saved as string.

image-20220319154006697

(4) View database

Execute the commands add mirgration and update database

image-20220319154259089

(5) Simplify the comparison of value objects

ExpressionHelper

class ExpressionHelper
{
	public static Expression> MakeEqual
(Expression> propAccessor, TProp? other)
	where TItem : class where TProp : class
	{
		var e1 = propAccessor.Parameters.Single();
		BinaryExpression? conditionalExpr = null;
		foreach (var prop in typeof(TProp).GetProperties())
		{
			BinaryExpression equalExpr;
			object? otherValue = null;
			if (other != null)
			{
				otherValue = prop.GetValue(other);
			}
			Type propType = prop.PropertyType;
			var leftExpr = MakeMemberAccess(propAccessor.Body, prop);
			Expression rightExpr = Convert(Constant(otherValue), propType);
			if (propType.IsPrimitive)
			{
				equalExpr = Equal(leftExpr, rightExpr);
			}
			else
			{
				equalExpr = MakeBinary(ExpressionType.Equal,
					leftExpr, rightExpr, false,
					prop.PropertyType.GetMethod("op_Equality")
				);
			}
			if (conditionalExpr == null)
			{
				conditionalExpr = equalExpr;
			}
			else
			{
				conditionalExpr = AndAlso(conditionalExpr, equalExpr);
			}
		}
		if (conditionalExpr == null)
		{
			throw new ArgumentException("There should be at least one property.");
		}
		return Lambda>(conditionalExpr, e1);
	}
}

image-20220319154635087

Recommended Today

Introduction to js decorator (introduction to ts decorator)

reference js decorator @Decorator Decorator – Ruan Yifeng TS Decorator(2): Metadata TS Documentation – Decorators hint TypeScript has fully implemented the decorator, and the js decorator is a syntax that is still in the proposal. If you use js instead of ts, you need to configure Babel to use it. The prerequisite knowledge you need […]