No backing field could be found for property 'Email.EmployeeId' and the property does not have a getter
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
### Bug description
The following error happens during data seeding when querying the entity by the value type property.
```
No backing field could be found for property 'Email.EmployeeId' and the property does not have a getter for value type
```
The Employee Model:
```
using System;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using CleanHr.Domain.Aggregates.DepartmentAggregate;
using CleanHr.Domain.Exceptions;
using CleanHr.Domain.Primitives;
using CleanHr.Domain.ValueObjects;
namespace CleanHr.Domain.Aggregates.EmployeeAggregate;
public sealed class Employee : AggregateRoot
{
internal Employee(
IDepartmentRepository departmentRepository,
IEmployeeRepository employeeRepository,
EmployeeName name,
Guid departmentId,
DateOfBirth dateOfBirth,
Email email,
PhoneNumber phoneNumber)
{
Id = Guid.NewGuid();
SetName(name);
SetDepartmentId(departmentRepository, departmentId);
SetDateOfBirth(dateOfBirth);
SetEmail(employeeRepository, email);
SetPhoneNumber(employeeRepository, phoneNumber);
CreatedAtUtc = DateTime.UtcNow;
}
// This is needed for EF core query mapping and serialization.
[JsonConstructor]
private Employee()
{
}
public EmployeeName Name { get; private set; }
public Guid DepartmentId { get; private set; }
public DateOfBirth DateOfBirth { get; private set; }
public Email Email { get; private set; }
public PhoneNumber PhoneNumber { get; private set; }
public bool IsActive { get; set; }
public DateTime CreatedAtUtc { get; }
public DateTime? LastModifiedAtUtc { get; private set; }
// Navigation Properties
public Department Department { get; private set; }
// Public methods
public void SetName(EmployeeName name)
{
Name = name ?? throw new DomainValidationException("The name cannot be null.");
}
public void SetDateOfBirth(DateOfBirth dateOfBirth)
{
DateOfBirth = dateOfBirth ?? throw new DomainValidationException("The dateOfBirth cannot be null.");
}
public async Task SetDepartmentAsync(IDepartmentRepository repository, Guid departmentId)
{
ArgumentNullException.ThrowIfNull(repository);
if (departmentId == Guid.Empty)
{
throw new DomainValidationException("The departmentId cannot be empty guid.");
}
if (DepartmentId != Guid.Empty && DepartmentId.Equals(departmentId))
{
return;
}
bool isDepartmentExistent = await repository.ExistsAsync(d => d.Id == departmentId);
if (isDepartmentExistent == false)
{
throw new DomainValidationException($"The Department does not exist with the id value: {departmentId}");
}
DepartmentId = departmentId;
}
public async Task SetEmailAsync(IEmployeeRepository repository, Email email)
{
ArgumentNullException.ThrowIfNull(repository);
if (email == null)
{
throw new DomainValidationException("The email cannot be null.");
}
if (Email != null && Email.Value.Equals(email.Value, StringComparison.OrdinalIgnoreCase))
{
return;
}
bool isPhoneNumberExistent = await repository.ExistsAsync(d => d.Email == email);
if (isPhoneNumberExistent)
{
throw new DomainValidationException("An employee already exists with the provided email.");
}
Email = email;
}
public async Task SetPhoneNumberAsync(IEmployeeRepository repository, PhoneNumber phoneNumber)
{
ArgumentNullException.ThrowIfNull(repository);
if (phoneNumber == null)
{
throw new DomainValidationException("The phoneNumber cannot be null.");
}
if (PhoneNumber != null && PhoneNumber.Value.Equals(phoneNumber.Value, StringComparison.OrdinalIgnoreCase))
{
return;
}
bool isPhoneNumberExistent = await repository.ExistsAsync(d => d.PhoneNumber == phoneNumber);
if (isPhoneNumberExistent)
{
throw new DomainValidationException("An employee already exists with the provided phone number.");
}
PhoneNumber = phoneNumber;
}
private void SetDepartmentId(IDepartmentRepository repository, Guid departmentId)
{
SetDepartmentAsync(repository, departmentId).GetAwaiter().GetResult();
}
private void SetEmail(IEmployeeRepository repository, Email email)
{
SetEmailAsync(repository, email).GetAwaiter().GetResult();
}
private void SetPhoneNumber(IEmployeeRepository employeeRepository, PhoneNumber phoneNumber)
{
SetPhoneNumberAsync(employeeRepository, phoneNumber).GetAwaiter().GetResult();
}
}
```
The Email Value Type
```
using System.Collections.Generic;
using System.Text.RegularExpressions;
using CleanHr.Domain.Exceptions;
using CleanHr.Domain.Primitives;
namespace CleanHr.Domain.ValueObjects;
public sealed class Email : ValueObject
{
private const int _maxLength = 50;
public Email(string value)
{
SetValue(value);
}
public string Value { get; private set; }
protected override IEnumerable GetEqualityComponents()
{
yield return Value;
}
private void SetValue(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new DomainValidationException("The Email cannot be null or empty.");
}
if (value.Length > _maxLength)
{
throw new DomainValidationException($"The Email length must be less than {_maxLength + 1} characters.");
}
Regex emailRegex = new(@"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$");
Match match = emailRegex.Match(value);
if (match.Success == false)
{
throw new DomainValidationException("The Email value is not a valid email.");
}
Value = value;
}
}
```
The Employee Model Configuration:
```
using CleanHr.Domain.Aggregates.EmployeeAggregate;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CleanHr.Persistence.RelationalDB.EntityConfigurations.EmployeeAggregate;
public class EmployeeConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.ToTable("Employees");
builder.HasKey(emp => emp.Id);
builder.OwnsOne(emp => emp.Name).Property(n => n.FirstName)
.HasColumnName("FirstName").HasMaxLength(50).IsRequired();
builder.OwnsOne(emp => emp.Name).Property(n => n.LastName)
.HasColumnName("LastName").HasMaxLength(50).IsRequired();
builder.Navigation(emp => emp.Name).IsRequired();
builder.HasOne(emp => emp.Department).WithMany().HasForeignKey(emp => emp.DepartmentId).IsRequired();
builder.OwnsOne(emp => emp.DateOfBirth)
.Property(d => d.Value).HasColumnName("DateOfBirth").HasColumnType("date");
builder.Navigation(emp => emp.DateOfBirth).IsRequired();
builder.OwnsOne(emp => emp.Email)
.Property(e => e.Value).HasColumnName("Email").HasMaxLength(50).IsRequired();
builder.Navigation(emp => emp.Email).IsRequired();
builder.OwnsOne(emp => emp.PhoneNumber)
.Property(p => p.Value).HasColumnName("PhoneNumber").HasMaxLength(15).IsRequired();
builder.Navigation(emp => emp.PhoneNumber).IsRequired();
}
}
```
The line causes problem during the data seeding
```
bool isPhoneNumberExistent = await repository.ExistsAsync(d => d.Email == email);
```
### Your code
```csharp
Code provided in the description.
```
### Stack traces
```text
System.InvalidOperationException: No backing field could be found for property 'Email.EmployeeId' and the property does not have a getter.
at Microsoft.EntityFrameworkCore.Metadata.IPropertyBase.GetMemberInfo(Boolean forMaterialization, Boolean forSet)
at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrPropertyGetterFactory.GetMemberInfo(IPropertyBase propertyBase)
at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrAccessorFactory`1.CreateBase(IPropertyBase propertyBase)
at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrPropertyGetterFactory.Create(IPropertyBase property)
at Microsoft.EntityFrameworkCore.Metadata.RuntimePropertyBase.<>c.b__51_0(RuntimePropertyBase property)
at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)
at Microsoft.EntityFrameworkCore.Metadata.RuntimePropertyBase.Microsoft.EntityFrameworkCore.Metadata.IPropertyBase.GetGetter()
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.ParameterValueExtractor[T](QueryContext context, String baseParameterName, List`1 complexPropertyChain, IProperty property)
at lambda_method104(Closure, QueryContext)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteCore[TResult](Expression query, Boolean async, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.AnyAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
```
### Verbose output
```text
```
### EF Core version
10.0.0
### Database provider
Microsoft SQL server
### Target framework
.NET 10.0
### Operating system
Mac OS 26.2
### IDE
VSCode latest
Contributor guide
Assessment
This issue has not been assessed yet.