C
C#2y ago
Dropps

❔ efcore and repositorys

in what scenarios does it make sense to use repositorys withhin ef core? i have a api layer project and a application layer project now my question is do i expose the dbcontext to the api project directly or should i introduce a repository pattern? i generally dont think it makes much sense to use repositorys as efcore basically has its own implementation for that with the DbSet<T> classes what are your suggestions?
33 Replies
JakenVeina
JakenVeina2y ago
when you get value from having an abstraction between your business logic and data access logic most notably for mocking/testing
Omnissiah
Omnissiah2y ago
is it more convenient to remake all the crud methods (and the queries) in repositories to mock the access or just mock the dbcontext?
Dropps
DroppsOP2y ago
i wont be doing mocking or testing on this project probably
JakenVeina
JakenVeina2y ago
do you have a lot of data access complexity? and potential reuse?
Dropps
DroppsOP2y ago
a lot yes i have about 300 tables currently in dapper migrating to ef core
JakenVeina
JakenVeina2y ago
then you might consider a repository layer of some kind me, I see value in isolating data access logic from business logic, particularly with EF and the IQueryable<T> interface building queries with IQueryable<T> can be verbose and unintuitive the difference between a query that performs ideally and one that performs like garbage can be extremely subtle having methods whose sole-purpose is "build a query to match parameters A, B, and C" can be valuable I also find it a good way to help manage complex schemas if creating entity A involves creating related records B and C, putting those together into one data access method can help ensure that no one in the business layer forgets to deal with B and C recent example: although, this is a non-database project, so mechanically, completely different I think it still kinda illustrates the point
public IObservable<AccountMutatedEvent> Mutate(IObservable<MutationModel> mutateRequested);
public IObservable<AccountMutatedEvent> Mutate(IObservable<MutationModel> mutateRequested);
public record MutationModel
{
public ulong AccountId { get; init; }

public ulong? ParentAccountId { get; init; }

public string? Description { get; init; }

public required string Name { get; init; }

public required Type Type { get; init; }
}
public record MutationModel
{
public ulong AccountId { get; init; }

public ulong? ParentAccountId { get; init; }

public string? Description { get; init; }

public required string Name { get; init; }

public required Type Type { get; init; }
}
public class AccountMutatedEvent
: AuditedActionEvent
{
public required VersionEntity NewVersion { get; init; }

public required VersionEntity OldVersion { get; init; }
}
public class AccountMutatedEvent
: AuditedActionEvent
{
public required VersionEntity NewVersion { get; init; }

public required VersionEntity OldVersion { get; init; }
}
public record VersionEntity
{
public required ulong Id { get; init; }

public required ulong AccountId { get; init; }

public required ulong CreationId { get; init; }

public ulong? PreviousVersionId { get; init; }

public ulong? ParentAccountId { get; set; }

public string? Description { get; set; }

public required string Name { get; set; }

public required Type Type { get; set; }
}
public record VersionEntity
{
public required ulong Id { get; init; }

public required ulong AccountId { get; init; }

public required ulong CreationId { get; init; }

public ulong? PreviousVersionId { get; init; }

public ulong? ParentAccountId { get; set; }

public string? Description { get; set; }

public required string Name { get; set; }

public required Type Type { get; set; }
}
with this setup, I have a data access layer that clearly defines what mutations CAN be made to an account and a method for performing them there are several ways a mutation can happen up in the UI MutationModel defines what CAN be changed and Mutate() performs it, with related operations this mutation operation includes A) correctly retrieving the current version (not as trivial as you might think) B) creating the new version that points back to the prior one C) creating an associated auditing record
Dropps
DroppsOP2y ago
my question is when i have the ef DbSet<T> for everything then why would i need the repository? wouldnt it make more sense to directly shove it into the api and the api accesses on controller level the needed stuff with things like _dbcontext.Users.SingleOrDefault(x => x.id == request.id) for things like GET - /api/v1/users/
JakenVeina
JakenVeina2y ago
depends on your scenario if you think that makes the most sense for you, go for it if you think it makes sense to have a separate layer, go for it if you think it makes sense to do a little of both, go for it
Dropps
DroppsOP2y ago
for mocking and testing i can still use something like
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: "v1_testDb")
.Options;
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: "v1_testDb")
.Options;
and fill in test data with
using (var context = new AppDbContext(options))
{
context.Users.Add(new User {Id = 1, Username = "Mister 1" });
context.Users.Add(new User {Id = 2, Username = "Miss 2" });
context.Users.Add(new User {Id = 3, Username = "Miss 3" });
context.SaveChanges();
}
using (var context = new AppDbContext(options))
{
context.Users.Add(new User {Id = 1, Username = "Mister 1" });
context.Users.Add(new User {Id = 2, Username = "Miss 2" });
context.Users.Add(new User {Id = 3, Username = "Miss 3" });
context.SaveChanges();
}
JakenVeina
JakenVeina2y ago
yeah, I find that to be a horrific pain in the ass a pain in the ass made slightly easier by not having business logic mixed together with data access logic
Dropps
DroppsOP2y ago
so there isnt really a convention about that?
JakenVeina
JakenVeina2y ago
lol, depends on who you ask some will vehemently say "never use repositories with EF" some will espouse the RepositoryBase<T> pattern
Bladesfist
Bladesfist2y ago
Like all absolute positions it's a pretty bad take
Dropps
DroppsOP2y ago
i guess thats fine then i like the idea of repositorys but i dont like it withhin ef due to the second level repo basically
JakenVeina
JakenVeina2y ago
that's generally the argument against them
Dropps
DroppsOP2y ago
i guess i inject it directly on the api level but have the logic on the app level
JakenVeina
JakenVeina2y ago
with EF
Dropps
DroppsOP2y ago
i can refactor when needed
Omnissiah
Omnissiah2y ago
maybe instead of putting dbcontext in the controller just use a unit of work in the middle
JakenVeina
JakenVeina2y ago
yeah, if you're going for a REST API, you probably won't HAVE much business logic in the API layer 6 of one, half-dozen of the other
Dropps
DroppsOP2y ago
iam refactoring an old rest api i made privately for my discord bot as i have the logic on scalable servers and the discord accessor just plugs into the discord api distributing it over the rest api
JakenVeina
JakenVeina2y ago
kinda amounts to the same thing
Dropps
DroppsOP2y ago
iam following straightly the nick chapsas course but hes using repositorys and dapper with postgres meanwhile iam ef core no repostitories and mssql
Bladesfist
Bladesfist2y ago
This series of blogs has a bunch of info on the pain points of both using and not using a repository pattern with EF Core https://www.thereformedprogrammer.net/is-the-repository-pattern-useful-with-entity-framework-core/
JakenVeina
JakenVeina2y ago
more power to ya
Dropps
DroppsOP2y ago
lets see what the blog has to say
Bladesfist
Bladesfist2y ago
Although I don't agree with the writer that In Memory databases solve the hard to mock problem That just gives you a new set of problems
Dropps
DroppsOP2y ago
didnt read it fully yet but this made me question is a repository at the end really a overhead measureable on performance?
TL;DR – summary

No, the repository/unit-of-work pattern (shortened to Rep/UoW) isn’t useful with EF Core. EF Core already implements a Rep/UoW pattern, so layering another Rep/UoW pattern on top of EF Core isn’t helpful.

A better solution is to use EF Core directly, which allows you to use all of EF Core’s feature to produce high-performing database accesses.
TL;DR – summary

No, the repository/unit-of-work pattern (shortened to Rep/UoW) isn’t useful with EF Core. EF Core already implements a Rep/UoW pattern, so layering another Rep/UoW pattern on top of EF Core isn’t helpful.

A better solution is to use EF Core directly, which allows you to use all of EF Core’s feature to produce high-performing database accesses.
Bladesfist
Bladesfist2y ago
Depends on how you use it You could put an optimized but otherwise redundant query in for a specifc scenario if perf testing showed it to be an issue I recently decided that the pros outweighed the cons for a new project as I don't want to test against a different DB to the one I am using in production. So repository pattern with mocked data layer for unit tests and actual DB created via docker testcontainers for integration testing
Dropps
DroppsOP2y ago
seems good thanks a lot
Omnissiah
Omnissiah2y ago
(a repository with a specific query would be a query object pattern more than a repository pattern)
JakenVeina
JakenVeina2y ago
see, therein lies the problem "repository pattern" is itself almost meaningless, cause it can mean too many different things do what makese sense to you
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server