Search code examples
c#domain-driven-designcontainsddd-repositoriesddd-service

How to Use Contains a vlaue object type in DDD repository?


I have a project that is designed with pattern Domain Driven Design. But I have a problem in using ‘Contain’ in this design.

I have an entity that has a "name" field that is of type "StudentName". The type is the value object.

public class Student : LongBaseAggregateRoot
{
    public StudentName Name { get; protected set; }
    public Student(StudentName name)
    {
        Name = name;
        
    }
}

I wrote the value object as follows:

public class StudentName: BaseValueObject<StudentName>
{
    public string Value { get; private set; }

    private StudentName (string value)
    {
        if (value is null)
            throw new Exception();

        value = value.Trim();

        if (value == string.Empty)
            throw new Exception();

        Value = value;
    }

    public override bool IsEqual(StudentName otherObject)
    {
        return Value == otherObject.Value;
    }

    public override int ObjectGetHashCode()
    {
        return Value.GetHashCode();
    }

    public static StudentName GetInstance(string value)
    {
        return new(value);
    }

    public static implicit operator string(StudentName value)
    {
        return value.Value;
    }
}

Its configuration file is as follows:

public class StudentConfig : IEntityTypeConfiguration<Student>
{
    private readonly int studentNameMaxLen = 300;

    public void Configure(EntityTypeBuilder<Student> builder)
    {
        builder.Property(x => x.Name).HasConversion(b => b.Value, b => StudentName.GetInstance(b)).IsRequired().HasMaxLength(studentNameMaxLen);
        
    }
}

Now I want to select ‘name’ using ‘Contains’. Like the code below.

public async Task<ListResponse<IList<GetStudentsDto>>> GetStudents(GetStudentsQuery param)
{
    var query =  _repository.AsQueryable();
    
    query = query.Where(x => x.Name.Value.Contains(param.Name));
    var result =await query.ToList<Student, GetStudentsDto>(_mapper, param);

    return result;
}

After searching, I realized that if I use ‘Ownsone’ in my configurations file, the problem of using Contains will be solved And I did it. I changed the configuration file as follows:

builder.OwnsOne(x => x.Name).Property(x => x.Value).HasColumnName(nameof(Student.Name)).IsRequired();

Now I have a new error. I think it is because of deleting ‘HasConversion’ in the configurations file.

System.InvalidOperationException: 'No suitable constructor was found for entity type 'Student'. The following constructors had parameters that could not be bound to properties of the entity type: Cannot bind 'name' in 'Student(StudentName name)'

Note that only mapped properties can be bound to constructor parameters. Navigations to related entities, including references to owned types, cannot be bound.

And I couldn't put 'HasConversion' and 'OwnsOne' together in the configuration file.

The error of using HasConversion and OwnsOne at the same time


Solution

  • Sometimes it helps to have private empty constructors in your entity classes to help entity framework to map the properties of the entities. Because of it being private, your entities will still be protected from being constructed without parameters by outside classes.