I'm building a DDD application with CQRS.
In my aggregate I have a Budget
aggregate root and BudgetedTransaction
entity.
Budget
keeps track of the transactions, the list of BudgetedTransactions
is the property of the Budget
.
In the database that's a 1-to-many relationship between the Budget
and BudgetedTransaction
.
My Budget
class:
public class Budget : Entity, IAggregateRoot
{
private readonly List<BudgetedTransaction> _budgetedTransactions;
private Budget() { } //ef core
public Budget(List<BudgetedTransaction> budgetedTransactions)
{
_budgetedTransactions = budgetedTransactions;
}
public void AddBudgetedTransaction(BudgetedTransaction transaction)
{
_budgetedTransactions.Add(transaction);
}
}
In my command handler I'm using EF core to retrieve the Budget
, along with the transactions using Include
method in the repo
public async Task<BudgetedTransactionDto> Handle(AddBudgetedTransactionCommand command, CancellationToken cancellationToken)
{
var budget = await _budgetRepository.GetBudgetById(command.AddBudgetedTransactionRequest.BudgetId);
var transaction = new BudgetedTransaction(
_guidService.NewGuid,
command.AddBudgetedTransactionRequest.BudgetId,
command.AddBudgetedTransactionRequest.BudgetingCategoryId,
command.AddBudgetedTransactionRequest.TransactionType,
command.AddBudgetedTransactionRequest.TransactionOccurrences,
command.AddBudgetedTransactionRequest.StartDate,
command.AddBudgetedTransactionRequest.Amount,
_dateTimeService.UtcNow);
budget.AddBudgetedTransaction(transaction);
await _budgetRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
budget.AddDomainEvent(new BudgetedTransactionAdded(transaction));
return await _transactionsRepositoryRead.GetBudgetedTransactionById(transaction.Id);
}
BudgetRepository
:
internal class BudgetRepository : IBudgetRepository
{
private readonly BudgetingDbContext _dbContext;
public BudgetRepository(BudgetingDbContext dbContext)
{
_dbContext = dbContext;
}
public IUnitOfWork UnitOfWork => _dbContext;
public async Task<Budget> GetBudgetById(Guid budgetId)
{
return await _dbContext.Budgets
.Where("BudgetedTransactions")
.Include(b => b.)
.SingleAsync();
}
}
And finally my entity configuration. For the migration purposes I managed to avoid adding the navigation property to Budget
.
public class BudgetedTransactionEntityTypeConfiguration : IEntityTypeConfiguration<BudgetedTransaction>
{
public void Configure(EntityTypeBuilder<BudgetedTransaction> builder)
{
builder
.ToTable("BudgetedTransaction")
.HasKey(t => t.Id);
builder
.Property<Guid>("Id")
.IsRequired();
builder.HasOne<Budget>()
.WithMany();
builder
.Property<Guid>("BudgetId")
.HasColumnType("uniqueidentifier")
.IsRequired();
}
}
However I'm getting the exception when retrieving the budget from DB: System.InvalidOperationException: An error was generated for warning 'Microsoft.EntityFrameworkCore.Query.InvalidIncludePathError': Unable to find navigation 'BudgetedTransactions' specified in string based include path 'BudgetedTransactions'. This exception can be suppressed or logged by passing event ID 'CoreEventId.InvalidIncludePathError' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
I understand the problem, I'm providing the name of non-existing navigation property.
Is it then otherwise possible to load the Budget
with Transactions
and not create the navigation property?
If you want use navigational properties as include
, must add it as public
public Budget {
....
private readonly List<BudgetedTransaction> _budgetedTransactions;
public IEnumerable<BudgetedTransaction> BudgetedTransactions => _budgetedTransactions?.ToList()
....
}