Yak: csharp before situation (Yak 01) — DB-backed domain classes
Add the C# BEFORE situation: a fake-EF DbContext + DbBase that Client/Order inherit, the active-record DbContext back-reference (the dependency leak), navigation fix-up on Client.Orders, and a WebApp that consumes the domain classes directly. Adds an empty After project so the solution shape is final, and an xUnit BeforeTests suite covering the required assertions. - db-subclass-to-dto.sln (classic .sln; SDK 10 defaults to .slnx which breaks run-tests.sh) - src/Before: DbBase, DbContext, Client (+ClientOrders fix-up), Order, WebApp - src/After: empty placeholder (filled in Yak 02) - tests/BeforeAfter.Tests/BeforeTests.cs: 8 tests (7 required + 1 companion)
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
namespace Before;
|
||||
|
||||
/// <summary>
|
||||
/// A tiny hand-rolled fake of EF's <c>DbContext</c>: it keeps a change-tracker
|
||||
/// (a registration collection of the <see cref="DbBase"/> entities it knows
|
||||
/// about) and a <see cref="Save"/> that fakes <c>SaveChangesAsync</c>.
|
||||
///
|
||||
/// This is deliberately NOT real EF Core — no providers, no SQLite, no NuGet
|
||||
/// beyond xUnit. The point of the exercise is the *shape of the dependencies*
|
||||
/// (domain object -> DbContext), not EF's behaviour.
|
||||
/// </summary>
|
||||
public class DbContext
|
||||
{
|
||||
// The change tracker: the set of entities this context is responsible for.
|
||||
// (EF calls this its change tracker; a list stands in for the
|
||||
// id -> entity registration dictionary.)
|
||||
private readonly List<DbBase> _tracked = new();
|
||||
|
||||
/// <summary>
|
||||
/// Attach an entity to this context (EF's <c>Add</c>). Sets the
|
||||
/// active-record back-reference so the entity knows its owner.
|
||||
/// </summary>
|
||||
public void Attach(DbBase entity)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(entity);
|
||||
entity.DbContext = this;
|
||||
if (!_tracked.Contains(entity))
|
||||
_tracked.Add(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fake <c>SaveChangesAsync</c>: walk the tracked entities and assign a
|
||||
/// fresh <see cref="Guid"/> to any whose id is still <see cref="Guid.Empty"/>.
|
||||
/// Returns the number of entities that were (re)saved — i.e. newly
|
||||
/// identified — mirroring EF's "rows written" return value.
|
||||
/// </summary>
|
||||
public int Save()
|
||||
{
|
||||
var saved = 0;
|
||||
foreach (var entity in _tracked)
|
||||
{
|
||||
if (entity.Id == Guid.Empty)
|
||||
{
|
||||
entity.Id = Guid.NewGuid();
|
||||
saved++;
|
||||
}
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
/// <summary>True if <paramref name="entity"/> is in this context's tracker.</summary>
|
||||
public bool IsTracked(DbBase entity) => _tracked.Contains(entity);
|
||||
|
||||
/// <summary>Read-only view of the entities this context currently tracks.</summary>
|
||||
public IReadOnlyList<DbBase> Tracked => _tracked;
|
||||
}
|
||||
Reference in New Issue
Block a user