"Repositories in .NET Only Need Two Methods!" | Code Cop

2024 ж. 21 Сәу.
39 785 Рет қаралды

Until the 30th of April, use code BIRTHDAY40 for 40% off any course, BIRTHDAY20 for 20% off any bundle and BIRTHDAY15 for 15% off your first year of Dometrain Pro: bit.ly/4aUVR8l
Become a Patreon and get special perks: / nickchapsas
Hello, everybody, I'm Nick, and in this video of Code Cop I will take a look at LinkedIn advice that tells people to use just two methods in their repository pattern in .NET.
Workshops: bit.ly/nickworkshops
Don't forget to comment, like and subscribe :)
Social Media:
Follow me on GitHub: github.com/Elfocrash
Follow me on Twitter: / nickchapsas
Connect on LinkedIn: / nick-chapsas
Keep coding merch: keepcoding.shop
#csharp #dotnet

Пікірлер
  • You missed one crucial issue with the approach in the linkedin post. When you use Func instead of Expression you are going to use the IEnumerable Where, NOT IQueryable where. So the whole table gets loaded to memory and then the filter happens. This is the single major reason why you shouldn't do that.

    @markovcd@markovcd17 күн бұрын
    • This post should be pinned. For those who do not know, Expression is not compiled. It is still an expression tree which you can read and then can then be turned into compiled IL... or into a query or a query part. A Func is compiled, it can't be converted into anything else.

      @warny1978@warny197817 күн бұрын
    • You beat me to it. Well said!

      @alexbarac@alexbarac17 күн бұрын
    • Very valid point!

      @nickchapsas@nickchapsas17 күн бұрын
    • Great point. I have personally done this - accidentally used Func instead of Expression and was wondering, why my queries take so long :D

      @denisthedev@denisthedev17 күн бұрын
    • Came here to say exactly this. It is obvious that whoever came up with this recommendation not only doesn't know how the repository pattern works but is also completely oblivious to the most basic knowledge of EF.

      @ElvinGonzalez@ElvinGonzalez17 күн бұрын
  • "Hey, guys, I have a great idea. Let's simplify our repository layer and make up for it by further complicating our business logic layer!"

    @AmateurSpecialist@AmateurSpecialist17 күн бұрын
    • It does not even make it more complicated, it also can lead to code duplication. What is the sense of a repository that does not encapsulate the query logic? Now the consumer of this method needs to know everywhere on how to get a user by name that is not deleted for instance (x => x.Name == name && !x.IsDeleted). I don't want to have this logic spreaded everywhere.

      @Rookiande@Rookiande16 күн бұрын
    • @@Rookiande Obviously solve this problem by making an extension method for the repository layer. :3

      @AmateurSpecialist@AmateurSpecialist15 күн бұрын
  • I fell from my chair as soon as I read the title. Good thing this was an episode of Code Cop

    @kenlayug@kenlayug17 күн бұрын
  • Aaaaand, its not even an Expression, it's only a Func ... you're not gonna translate that to anything.

    @lyrion0815@lyrion081517 күн бұрын
  • Just a note, you should not use ef query method with raw Func, it doesn't have information for writing the SQL Query so querying all the data and filtering on the client side. You should use Expression instead, wich has the syntax tree definition of the lambda and can be parsed.

    @lucasteles42@lucasteles4217 күн бұрын
  • "We don't need repositories when we have EF"

    @bernapessoa@bernapessoa17 күн бұрын
  • This is an interesting one, you can tell what they were going for. You use a higher order function so can specify any query. That is not a bad thing to want to do, but it misunderstands what a repository is and why you would use it. A repository is a place where you put all your code for going between how a database stores a set of data and the data structure you use for that data in your code. You put it all in one place so that its not littered through out your code and so that any changes only have to happen in one place where its isolated and easy to understand. You make methods for all the different stuff you want, because you get code reuse from it and it specifies the contract that your application expects your database to adhere to. A repository uses an interface because this allows you to switch the database completely as long as you can still get the same data structure out of it. If you are using higher order functions, why bother with the interface? Your function parameter is the interface and if you are using this with an ORM, you have made an ORM to query your ORM with. Because the example, in the image, is not a real repository its a useless abstraction. You can just query the database directly if you want to. You can also query entity framework without a repository. Having a repository is a decision you make because you want to get something out of it. The example holds no benefit over a direct query.

    @tropictiger2387@tropictiger238717 күн бұрын
    • Ya. I always love that argument to swap dbs. I have been coding for 25 years and it has never happened. Except once I guess but that was EF core to mongodb in which case it was still useless. We always seem to design things to potentially swap out but never do. Instead I am going the microservices route to reduce api code to the size of a sprint or two so we can easily rewrite instead. This removes the nasty larger query methods that get created in larger services.

      @T___Brown@T___Brown17 күн бұрын
    • @@T___Brown I swapped out SQLite for LiteDB a few months back, but you are right it doesn't happen often. What I have benefited from is having the interface there when switching from EF6 to EF Core when we did the switch to .Net Core. For a while we passed our queries through a .Net Standard project that bridged the .Net Core project with the new EF Core repositories to the .Net framework project that was using the data. I think the argument is true but it gets overstated.

      @tropictiger2387@tropictiger238717 күн бұрын
  • when you start accepting predicates though, don't you end up with the same predicates repeated all through the codebase?

    @tehsimo@tehsimo17 күн бұрын
    • No. You add extension methods on that interface that provide searches by name, e-mail and so on. Sarcasm mode off :D

      @woocaschnowak@woocaschnowak17 күн бұрын
  • I feel like, if u pass a predicate like this to a repository, as you said, you imply that u'll always use EF core. But then, why not just inject EF Core DbContext directly in your service ? The repo just feels like an useless layer ?

    @prae9191@prae919117 күн бұрын
  • I am curious about this advice. It is only related to EF Core, but as we know DbSet is already generic repository, so why do we need extra one? XD

    @kurumi690@kurumi69017 күн бұрын
  • I totally understand why he proposed it, I myself have implemented some specification-pattern like query-objects but found them hard to maintain anyway so I switched to lengthier more explicit human-readable repo-functions like in the example you gave, thanks for the great post !

    @krislesage7884@krislesage788415 күн бұрын
  • We are using it on our code base, but Expression instead of Fun and we heavily relying on EF and EF core. Not great general advice, very true, but it could work for certain scenarios where you have a lot of flexibility in how the data is being retrieved by the calling side and you know EF is only being used.

    @dinodeman@dinodeman17 күн бұрын
  • Instead of using the Func predicate, use an expression tree. Expression tree can easily be converted to LINQ with Expression.Lambda extension, or converted to any other query format by simply traversing the tree. The expression tree can easily be produced from a DSQL. So you only need three methods on your repository contract that look something like, TEntity Read(T id), IEnumerable Read

    @7th_CAV_Trooper@7th_CAV_Trooper17 күн бұрын
  • Our repositories accept our business models, then convert them to the data models, which is what EF works with. Any data retrieved, updated, created, is again converted to a business model and returned from the repository method. We try to avoid leaking the data model into the services.

    @chriscardoz@chriscardoz17 күн бұрын
    • Which prevents a user to pass silly things through the repository method and get something he/she is not supposed to get or to do.

      @warny1978@warny197817 күн бұрын
    • Same. This also allows us to have data/entity models with metadata that shouldn't be returned to the business models.. For me it's either going all out like that or just ignoring the repository pattern and using the db context directly in the application/service layer, because that middle ground doesn't provide much value IMO.

      @ChristofferLund@ChristofferLund17 күн бұрын
    • Dont you get a complicated mapping layer in the repository? EF normally does the change tracking, so now you need to map new entities to new rows, updated to updated rows (manually copy all values), and manually find out what got removed and then delete the rows...

      @Rein______@Rein______17 күн бұрын
    • This is exactly the reason I thought .. why is that bad (edit: when reading the title) ? Our repos have 2 methods: Public Task Load(aggregateKey) and Public Task Save(aggregate) ...

      @ianluyten1210@ianluyten121016 күн бұрын
  • I don't use repositories because I use EF Core, and DbContext is already a repository. Any repository that I place on top of DbContext will be a shittier, handicapped version of DbContext that won't have all EF Core features available to it and will force me to write 5x more code for no benefit whatsoever.

    @Kotz_en@Kotz_en17 күн бұрын
  • Using func instead of expression as a parameter makes no sense

    @nordBelka@nordBelka17 күн бұрын
  • I have done this pattern myself, but I would add, only in the EF Core context, and with the pretty solid understanding that our database layer was going to be SQL Server. Yes, I would acknowledge that if the database would need to change, that could create future problems, but given the tech stack we have, that is pretty remote. If we were more dynamic and prone to change, I might seriously reconsider it. But it's also good to get an insight from a different end of the dev spectrum.

    @KnightSwordAG@KnightSwordAG17 күн бұрын
  • I like this series a lot and learn from it. Thanks.😄

    @ensar8177@ensar817717 күн бұрын
  • Finally! New UI! :-D

    @KrInMotion@KrInMotion17 күн бұрын
  • Great advice. A few months back I had the same thought, to use predicates, for a repository. After thinking through it, though, I rejected it for the reasons you outlined here.

    @Dave0zz@Dave0zz17 күн бұрын
  • Why use the repository pattern when you pretty much write entire query as "predicate" in service? At that point write whole IQueryble in service

    @vyteniskajackas7579@vyteniskajackas757917 күн бұрын
    • Yep. I'm fighting with a service built that way right now. The EF DbContext is used directly in the business logic. Writing the unit tests without a proper repository is awful!

      @dave7038@dave703817 күн бұрын
    • ​@@dave7038is there is any issues with creating a mock of DbContext instead of Repo?

      @vitaliishmidt3704@vitaliishmidt370416 күн бұрын
  • Related, if you want to see some other approaches and their pros and cons: kzhead.info/sun/a9Zxfdl-aGVraY0/bejne.html&ab_channel=dotnet

    @Ardalis@Ardalis17 күн бұрын
  • I completely agree - in my own project right now I am writing repositories that target one specific DB technology and takes advantage of those non-standard features which that DB provider has. If I were to change DB provider I would have to create new repositories by copying the existing ones then replacing non-standard syntax with standard syntax to get things running and then start testing and optimizing for the new DB. In my case I assumed that I was not going to change the DB for my project, so that cost won't be there, and I am very interested in performant queries which is why I want to control exactly what query code (SQL) is being run.

    @chralexNET@chralexNET14 күн бұрын
  • I think that the advice does more good than bad. For years now using similar approach to all CRUD operations and never looked back: var userModel = AutoProcedure.Of().GetBy(f => f.Token, model.Token); public TModel? GetBy(Expression propertyExpression, TProperty value, WherePattern? pattern = null, SqlOrderCondition? order = null) Dapper only, because good luck automating filters with EF. One generic virtual class - because why write same code for every model. No DI (because it sucks (yes, I said it)), because it's unusable in static context. Separate custom interface for any db query generation - full control over the process.

    @DreigoUndead@DreigoUndead17 күн бұрын
  • This is not really the repository pattern as described by Martin Fowler. A repository is supposed to simply be a collection like interface with a method to execute client defined queries against. Repositories are not meant to have a bunch of specific methods that encapsulate queries. Entity framework is a good example of the repository pattern.

    @simonwood4864@simonwood486417 күн бұрын
  • As for the Dapper vs EF debate, I'll generally start with Dapper, unless I know my application will require multiple tables with various relationships. To me, Dapper is so much easier to set up and flexible to use. Plus, I prefer the control I have over the SQL statements. It's a tradeoff between ease of setup (Dapper) and abstraction of complexity (EF). With complex data stores, EF is definitely the way to go.

    @JohnEBoldt@JohnEBoldt16 күн бұрын
  • Multiple places to translate to your external application (In this case database) is something you never would want. This also goes for predicates. I would prefer just using a separate class with fields that you can use to search for instead of just trying to leak the DA into your other classes. This separate class could also hold paging, ordering and what not. And if you need a more specific usecase you can make a separate method and or class for said scenario,. depending on your needs. This way its also easier to test said statements then just having statements everywhere. And I primarily use EF Core currently.

    @OxiddaGustha@OxiddaGustha17 күн бұрын
  • I prefer to use Specifications for these purposes.

    @volodymyr.r@volodymyr.r17 күн бұрын
  • In my repositories, i use a single read method, " AsQueryable," it points to the concrete implementation from the LINQ provider. This way, all reading is abstracted in a single generic method.

    @algorithm-artisan@algorithm-artisan17 күн бұрын
  • What theme are you using in Rider?

    @bingrocks1966@bingrocks196613 күн бұрын
  • Best poster frame ever 😛

    @andreaskarz@andreaskarz17 күн бұрын
  • Dev: "I'm gonna make this repository pattern to allow me to use different database engines. I'm gonna create all these interfaces, classes and tests. It will be awesome, and my application will be ready for every database engine." Then proceed to use only a single database engine for the entirety of the project. If think about the last time you needed to support SQL Server, MySql, SQLite, MongoDB, Redis and ElasticSearch in a single project for the same entities, you will find that 99% of the time you don't really need any repository pattern.

    @alejandrocoto7367@alejandrocoto736717 күн бұрын
  • Not trying to open the pandoras' box. But Repositories are very tricky. Either you go and abstract your queries but then end up with lots of read methods like on the "bad" example, or you leak all your queries to the "business" layer. I don't like Repositories if I'm already using EF or any other full fledge ORM, but in the cases that I do, I really like using specifications to encapsulate my queries. Worst than a Repository, only a GenericRepository :D

    @JoaoSilva-rz4js@JoaoSilva-rz4js17 күн бұрын
    • One can say - EF is a repository itself. But what about the specifications - don't they just the second approach from the video with Expression but wrapped in the specification class?

      @MrSupasonik@MrSupasonik16 күн бұрын
    • ​@@MrSupasonikyes. But you Said it. It's wrapped inside a class, belongs to the data "layer" and can be reused.

      @JoaoSilva-rz4js@JoaoSilva-rz4js16 күн бұрын
  • Perhaps this also applies to the Interface segregation principle from SOLID, we can implement helper methods through Extensions and we won't have to mock them all.

    @havendv@havendv17 күн бұрын
  • Hi Nick, I'm learning about EF core. But I don't know what is default loading in EF core, can you explain me about this?

    @vietquang6603@vietquang660315 күн бұрын
  • I prefer using repositories as meant by DDD, i.e. with domain-specific instead of crud operations. The only thing worse than GetUser/GetUsers with expressions would be a generic repo.

    @krccmsitp2884@krccmsitp288417 күн бұрын
  • I've thought about this before and actually believed i was more of a fan of the "GOOD" approach all though you make the argument that the translation layer from a predicate to various conditions etc. (based on the underlying data store) is hard and not an easy feat, i don't feel like that rules out other ways to abstract and implement the "GOOD" approach, why not make the "GOOD" approach with a generic Criteria class that perhaps can be built with a fluent builder or something like that? The only thing i dislike about the "BAD" approach which you seem to consider better is that it makes each entity interface very distinct and thus makes it harder to build pipelines, decorators etc. around it or in short treat them in a uniform way which just means you will end up writing a lot of code which is very similar, then when your project grows you will end up with a lot of code that was unnecessary (repetitive/similar) blending in with all the important business logic, or am i wrong? Would love to hear your opinion.

    @Velreine@Velreine17 күн бұрын
  • I would love to see a speedrun video, where you debunk more at once.

    @xopabyteh6543@xopabyteh654317 күн бұрын
  • Quick question, in trying to build a API, with clean code arquitecture, I have several Value object to encapsulate all the data validation, this value object should only be used in the services or all across the project (repositories...)?

    @marcosgarces7962@marcosgarces796217 күн бұрын
    • Not to be blunt, but how are you going to do Clean Code while violating DRY? Make that make sense. And sure, you CAN make the case that internally you only need DTO's, and VO's are only needed at the point of ingress of data. Reasonable. And then someone, somewhere internally, creates a DTO with invalid data (because reasons....) and the whole thing keels over because every part was expecting data integrity to be the VO's responsibility thus no further checks were made... If you want integrity, at the "boundary interconnect" you turn the VO into a DTO and egress it across. On the other side, you ingress the DTO, turn it into a VO, with implied validation, and then use the VO all across.

      @ErazerPT@ErazerPT17 күн бұрын
    • @@ErazerPT and how can I handle VO in EF?

      @marcosgarces7962@marcosgarces796217 күн бұрын
    • @@marcosgarces7962 Never used EF as i despise ORM's in general, but on an educated guess from MS's page, you can have EF use the VO's constructor with mapped parameters. Or, given it's a boundary layer, you could turn the VO'S into DTO's as they go into EF, and DTO'S into VO's as they come out of EF. Not very performant, but... that's Clean Code in a nutshell, very pretty for mental masturbation, very shitty for performance.

      @ErazerPT@ErazerPT17 күн бұрын
    • You create a "Composition root" layer that only contains interfaces, that are implemented on your various data objects, these interfaces are parameters for the source objects constructor. You only ever have the code once.

      @mortenbork6249@mortenbork624915 күн бұрын
  • Generic repository that exposes IQueryable or accepts Expression for use with IQueryable is a non-pattern. That is just a thin wrapper over DbContext that abstracts nothing. A repository needs to be an API for your data store.

    @gilbes1139@gilbes113917 күн бұрын
  • We don't use predicates in repositories. I use them in services though and only because it made my work easier at the time. It was still in early development of our app and things weren't set so I just wrote one function to update rows in a certain table. I can specify predicate to select rows, fields to be updated, update expressions (i. e: x => x.Name = "John") etc. It's handy but the performance is probably not the best (luckily we don't have to care about it NOW) and the readability too so it'll definitely need some refactor.

    @flash3621@flash362117 күн бұрын
  • Basically, it's a leaky abstraction because it's designed bottom up (from the implementation to the abstraction) rather than top down. On the other hand, it must be said that the need of a repository interface that can abstract a relational database, a document storage, a key storage, a ledger and maybe a database built on Minecraft isn't so much sought after anymore, so some "specialization" might be accepted. On the other hand, as someone else pointed out, delegates rather than expressions are a big big error. Personally, I like the specification pattern.

    @renatogolia211@renatogolia21117 күн бұрын
  • Nick finally moved to the new Jetbrains UI, it is so much better than the old one

    @antonmartyniuk@antonmartyniuk16 күн бұрын
  • The first time its not a horrible advice and there is a really good thing to learn.

    @garcipat@garcipat17 күн бұрын
  • Good to see a MicrosoftVP saying this Probably other MVPs should look at this too.

    @dariogriffo@dariogriffo16 күн бұрын
  • I have not seen the vid yet, and my bets are on either active records or repositories that are ripped apart into separate repositories where there are only actions for a single and actions for multiple entities.

    @LordErnie@LordErnie17 күн бұрын
    • I was wrong. It is much worse.

      @LordErnie@LordErnie17 күн бұрын
  • Bro updated his IDE

    @flashminat0@flashminat017 күн бұрын
  • I would never use the "good" methods, I prefer the "BAD" methods, that way I am sure the requests to the database works perfectly, even if I had to change data source

    @casperhansen826@casperhansen82617 күн бұрын
  • I use dapper to call stored procedures. It may or may not be the best way, but it’s what I’m comfortable with

    @saberint@saberint16 күн бұрын
    • Any use of any mapping tool, especially in C#, in my opinion violates good code practice. 1: You seriously can't smack an interface on your DTO objects to convert them into other objects? You NEED a framework for that? ---- wtf. 2: While something like EF might be considered worthy of your time, as you technically don't HAVE to understand SQL to use it, the minute performance is a requirement, you will be spending a literal fuck ton on a consultant, that can help you out of your extreme lack of knowledge. Simply learn SQL. While it is complicated, so is learning LINQ AND SQL. For that reason, again in my opinion EF is a bit silly. (It only really works, if performance is not an issue, and your employer, or you yourself, is comfortable burning money in the late stage of your development, rather in the early stage.) -> The alternative is writing your own DI and SOLID repository that is bespoke to whatever problem you are solving. 3: Frameworks in general are, just as a concept a bad idea, unless they are written in such a way, that the consumer of the framework, can inject their own code, into said frameworks. NO frameworks I have ever seen does this, as it breaks the way a producer of said framework can monetize it. Also, if the framework requires a syntax change, you are stuck re-writing your code, because some of party decided it was time to "mix it up". (Not just mappers here.) 4: Mappers must include the required information to solve a mapping issues, so must your own code, so why not write it in your own code? The "bloat" in C# and other OOP langauge can be mitigated. Not the point of the frameworked components, but close, and then you have no dependency on someone elses code base, that you can't extend, if it doesn't perform, or doesn't do PRECISELY what you want it to., 5: Taking a framework component into your system should be a carefully considered decision, that can be correctly anticipated in terms of cost, time, effort, maintenance and training. Most people just go "It's easier ? Okay then...." Software development is an engineering profession, could you stop, fucking it up? Please?

      @mortenbork6249@mortenbork624915 күн бұрын
  • very good point that this doesn't work for things like cosmos db, it's nice if you use entity framework with a sql db, but otherwise you will suffer with nosql solutions, especially those that require you to think very well about your data architecture, next to that your database model could be very different from the domain model, which is even harder to model with the predicate syntax

    @jacobkooijman5924@jacobkooijman592416 күн бұрын
  • The advise missings a thing that is with what technology. The repository specifications by Ardalis is really good there

    @javiergarciadelanoceda7708@javiergarciadelanoceda770817 күн бұрын
  • Have been using generic repos that accept specifications and projected expression. Promotes possible where clause reuse via the specification and reduces the fixed "includes" and the one-offs based on the select needs.

    @DJReRun@DJReRun17 күн бұрын
    • I would love to see an example of this :)

      @SuperLabeled@SuperLabeled17 күн бұрын
    • same would also like to see this

      @Velreine@Velreine17 күн бұрын
    • ​@@SuperLabeled My implementation is not in a public repo, but I did not invent combining these patterns either. If you Google "C# repository specification" the top returns will show examples of how they can work. I took what I found to be the best of several articles and added a parameter of type Expression Func for projection on my "Get" methods. So the "WHERE" portion of your query is handled by the specification and the "SELECT" by the projection. Instead of your individual repos expanding by additional one-off methods, you add specifications only as you need to define a new "WHERE". Not sure if that makes sense.

      @DJReRun@DJReRun17 күн бұрын
  • Callback parameterization generally makes the method more flexible, but hurts the readability. I once spent a good 5 minute trying to find what one function did and who wrote it, then realized that it was difficult to understand because it had so many indirections.

    @parlor3115@parlor311517 күн бұрын
  • It's cool, but with AI tools writing LINQ code doesn't take too much time also. Issue I see with Expression/Fund is it reduces readability. Thoughts?

    @eugenestein1629@eugenestein162914 күн бұрын
  • I tried this long time ago with dapper and after implementing 4-5 repos.. I switched back to traditional repos and ed core for data access. It's a pain to implement for other data stores

    @donnieimafidon8004@donnieimafidon800417 күн бұрын
  • Somehow I don't think that the people listening to the LinkedIn advice end up on Nick's channel

    @mannyb4265@mannyb426517 күн бұрын
  • The problem is that they're treating an abstraction layer as a direct pass through. The repository pattern has specific methods, as you mention at the end, to facilitate binding to the database of choice

    @MilesFlavel@MilesFlavel17 күн бұрын
  • I think some people get stuck in the purist "repository" mindset. "Data services" that get data in a particular way seems to be where so many projects end up, why fight it?

    @dennycrane2938@dennycrane293817 күн бұрын
  • Too general approaches are sometimes worse than specific ones. I mean, when you let your consumers to use a delegate (or an expression), you let them to pass ANYTHING to it. They can use whatever logic they want to and they will face the consequences of their actions only at the runtime. That is a common problem of EF approach for example. You write a predicate and then at the runtime realize, that it is not supported. Specific scenarios will mostly prevent such a problem to exist. From the very beginning you explicitly say what they can and what they can not do with your interface

    @yuGesreveR@yuGesreveR17 күн бұрын
  • wouldn't even comment this Linkedin post but as per your quest.. I prefer EF if I can choose as least for the intrinsic transaction operation.. ok not only for that but I have to admit that dapper has excellent performances

    @danieleluppi6648@danieleluppi664814 күн бұрын
  • Without watching the full video, and just until 0:48 then hit pause to type this.... Maybe I'm missing something, but in the example code shown at 0:48, why not just make the "good" advice part of a generic repo? If that's all it's going to do... Also would probably return a Queryable, but maybe I'm just weird...

    @Esgarpen@Esgarpen17 күн бұрын
  • IMO you only need to use repository on top of EF only if you plan to replace ORM with another one or write raw SQL in future.

    @brick2497@brick249717 күн бұрын
  • I use both EF and dapper depending on complexities and uses cases. I did use expressions in order to resuse the same IQuerable query in EF with different where clauses but it was in private functions in the concrete implementation using EF and not in the interface. I do not see why we would put an expression in a interface it goes against abstractions, correct me if im worng but it breaks the Liskov substitution principal

    @FrancisGauthier2@FrancisGauthier217 күн бұрын
    • No, it does not go against abstraction. Heck, delegates, aka function pointers are THE abstraction. You know NOTHING about that delegate but what it takes as input and what it outputs. How much more abstract can you go? Jokes aside obviously, because you can take in object, get out object and have no clue what did what or what it gave back. And there's reflection so it's kinda leaky... And no it does not break LSP which is strong BEHAVIORAL sub-typing in the sense that it does NOT produce undesirable (or unexpected) results. It does NOT mean you do the same thing, it means you produce the same results (without deleterious side effects). A simple example : Some method M1 of class A throws if it receives input x. The method M1 of class B that is a sub-type of A MUST throw if it receives x so that LSP is maintained. We make M1 so that it throws on input x, but now it affects field F of A that wasn't affected in the same way by M1 of A. LSP is NOT maintained. As Wikipedia so rightly puts it "Behavioural subtyping is undecidable in general" and really hard to achieve for anything but the most trivial of things... Objects that are "pure data" are trivial for LSP, everything else... it's a best effort at best.

      @ErazerPT@ErazerPT17 күн бұрын
  • Seems like leaking data layer details to the upper layers 😊

    @luxancap@luxancap17 күн бұрын
  • For me a "repository" is a DDD or at least the OOP concept. Many CRUD apps have got nothing to do with OOP. Procedural code, dtos everywhere. Repository IMO should have enough methods to supports the write side of the system, encapsulating logic about retrieving domain models, ideally agregates, into the memory. The problem is, people also use this for the query side and this is where the "repository" becomes a trash of methods that in many cases support one, two views, pulling far more data that is needed. It's not a repository imo, just call it IDataAccess or similar, or write your queries in handlers using raw sql or protections. Repository is not a must have patern for every project although for some people might sound sexy.

    @krzysztoffrydrych204@krzysztoffrydrych20414 күн бұрын
  • Maybe you could do this with a no sql db with very small partitions, that are mostly cached… A list of users might be in a single record in no SQL within a partition that only has data for one customer, and then you would iterate over it with the predicate. Still seems weird though, and missing a lot of context that indicates this was the authors intent. I feel like the author even managed to miss the biggest benefit of this strategy, which would be using a generic repository and implementing these methods in an abstract base class. That said I would probably just handle anything requiring a predicate in the service layer

    @NickSteffen@NickSteffen17 күн бұрын
  • it's 2024, we still discuss repositories lol

    @trukeis856@trukeis85617 күн бұрын
    • before GTA 6 !!!

      @Delphi80SVK@Delphi80SVK17 күн бұрын
    • And what is trendy to use in 2024?

      @tedchirvasiu@tedchirvasiu17 күн бұрын
  • If my repositories would accept a predicate, might as well use EF directly. The repository becomes a useless layer of indirection.

    @haxi52@haxi5217 күн бұрын
  • I guess I'll go with OData.

    @lohikal@lohikal13 күн бұрын
  • 1. As discussed in the video, almost certainly this advice is meant to be used with entity framework. Given that this point wasn’t discussed in the LinkedIn post makes me question the experience of the poster; have they ever _not_ worked with EF? 2. Given this, why even bother with a repository? What exactly is being abstracted? Just use the db context.

    @deezleguy@deezleguy17 күн бұрын
    • I often write smaller abstraction service classes that can be applied generically to types and use those. This way I can compose new services from them that perform more specific tasks. Basically, I take the abstraction of a Reader say, and have the more specific service pull it in. If the reader of that type needs more specific queries (either for performance or for specificity in the calling service) then it's just extended in the implementation to provide it. The Reader has a proxy to a DB context, and does all the queries through that. And the only reason why I have the proxy is so I don't need to cart around a DbContext mock in my tests.

      @KnightSwordAG@KnightSwordAG17 күн бұрын
    • I would just use the dbcontext directly but do try keep the IO at the edges. Testability though nowadays isn't a problem anymore.

      @Rick104547@Rick10454717 күн бұрын
  • New UI enabled? :)

    @jakosss@jakosss17 күн бұрын
  • I think it is also weird that the post's GOOD signatures missed the Expression. This tells me perhaps they don't know what they are talking about. Since even if you want to roll out your own translation layer, you need to parse that expression, a func would have no useful metadata for such a task

    @utubekade@utubekade17 күн бұрын
  • Nah, he was cooking, but it was inedible

    @noredine@noredine17 күн бұрын
  • Dapper user over here!

    @srokoszuio1@srokoszuio117 күн бұрын
  • Is there a way to achieve what the post was trying to do? Reduce the number of methods in repositories in an abstract way?

    @luckynikooo@luckynikooo17 күн бұрын
    • File 1 : interface IRepository { object Read ( object request ) ErrorType Write ( object request ) } File 2: class ResponseUser { public User user; public ErrorType error; } //! causes ResponseUser to be returned class RequestUserByName { public string name; } File 3: class ResponseOrder { public Order order; public ErrorType error; } class RequestOrdersByUser { public User user; } This is more prone to user error, but very extendable on both sides of the interface. Perhaps some kind of contract-system could be built on top

      @defeqel6537@defeqel653717 күн бұрын
  • Why are these repository methods not Task-based? In worst case methods could be a ValueTask-based for sync implementation in mocks.

    @_iPilot@_iPilot17 күн бұрын
  • It's not that you only need two methods... You don't need repositories at all!

    @tcortega@tcortega17 күн бұрын
  • I'm not a fan of using repositories myself. Mostly, I go for EF Core DbContext because I know my database won't change that wildly (what a pain if it happens to do). If I use a repository though (in some cases) I use a "filter" DTO with nullable properties, each one filtering for a specific field if not null.

    @alfonsosuarez9317@alfonsosuarez931717 күн бұрын
    • EF is a repository.

      @bartholomewtott3812@bartholomewtott381217 күн бұрын
    • @@bartholomewtott3812 Yes, but many people would still make a repository for a db context

      @alfonsosuarez9317@alfonsosuarez931717 күн бұрын
    • @@alfonsosuarez9317 DbContext is a strict repository. What nick is describing and everyone else here has nothing to do with it.

      @bartholomewtott3812@bartholomewtott381217 күн бұрын
    • @@bartholomewtott3812 I know, but half of my response does has to do by saying that I use DTOs to allow multiple optional filters and save creating another method sometimes.

      @alfonsosuarez9317@alfonsosuarez931717 күн бұрын
  • A method like "GetUserByAgeAndName" in a repository isn't strange to me at all. Maybe this specific one is kinda weird, but something like a "GetDocumentWhereOwnerAndYearAndStatus" I see often.

    @gileee@gileee17 күн бұрын
    • Care to elaborate/argure for this name? I would probably have called it "GetDocument(Person owner, Year year, DocumentStatus status)" or maybe even just "Get" if it is in a document repository.

      @gossigoss@gossigoss17 күн бұрын
    • @@gossigoss The name isn't the issue. If you're using something like JPA the actual method name (and it's params) is translated to the query. So it's unavoidable in that case if you're using that mechanism. It's just the fact that functions like that can and do exist. Even if a "GetUserByAgeAndName" might look strange from this POV. I just mentioned it because Nick said it was weird.

      @gileee@gileee17 күн бұрын
  • Not to mention testability. Whay are you going to test? All the predicates you pass in? You might as well use specific methods then as it more clearly describes what kinds of queries you need to support instead of having to go look at your probably less easily understandable tests.

    @TimHoekstra@TimHoekstra17 күн бұрын
  • And what about using Generic Repositories, what are the advices ? Dot you like our not like it ?

    @Leobraic@Leobraic17 күн бұрын
  • This example could just as well be used as an exemplar of ambiguous interface semantics. Is the "name" parameter supposed to take a wildcard? If so, then it's a shit name for the parameter, if not the method itself. If not, then why would it ever return a list, as opposed to a single object or Null? What this tells us is that even the author didn't have a sensible scenario where this proposed design should be used (or they would have shown it here)

    @davidmatten8519@davidmatten851916 күн бұрын
  • Also horrible because of bug fixing. If GetUserByAge returns something wrong, you fix it in one place. If you use predicates, then its very hard to detect where its used from.

    @CZProtton@CZProtton16 күн бұрын
  • Just incredibly strange advice. Repositories are just designed to encapsulate interaction with the database. And if you limit this interaction to just two database access points, then queries will be less optimal. For example, if we need to get only the user's name by their Id or only their age, then there is no need to extract all the column values from the database. This will lead to an unnecessary waste of time and unnecessary load on the database.

    @vorontsovru270895@vorontsovru27089517 күн бұрын
  • The point of a repository is lost if you do this. Might as well just abstract the whole db without the savechanges functionality at that point. This is just wrong, bad advice.

    @Looooooka@Looooooka17 күн бұрын
  • I run stored procs, what even is a repository? 😅

    @nicholaspreston9586@nicholaspreston958616 күн бұрын
  • For a real project, an approach such as a this should be filed under "YNGNI". Until such a time as this kind of functionality is called for, this is contrived, and isn't solving for a real need, at least not at the logic layer (the business logic layer) that it's being exposed, here. This may be some suitable facility in a middleware layer that provides more functionality on top of the bare storage layer (local caching, object distribution, etc), or maybe as part of protected facility for an abstract repository class but not at the layer exposed here. Why would a repository allow arbitrary queries at the BL layer?

    @davidmatten8519@davidmatten851916 күн бұрын
    • Because it's the BL layer that contains the logic? only it knows what it needs, repositories don't.

      @henriquesouza5109@henriquesouza510915 күн бұрын
  • Guess who has finally switched to the new UI

    @MatinDevs@MatinDevs17 күн бұрын
    • It's finally really nice

      @nickchapsas@nickchapsas17 күн бұрын
  • The other one I've seen is where someone just exposes IQueryable for the repository. It's just as silly/misguided and has the same downfalls. My stance on this for a long time is, avoid repositories. WAY too many mental cycles and arguments are wasted on repository abstractions when they aren't needed. The case where they are needed is when you have the requirement to support multiple different databases, then you absolutely need some kind of abstraction over your data access.

    @Denominus@Denominus17 күн бұрын
  • most examples I've seen recently of "new" design patterns are really anti-patterns.

    @colhountech@colhountech16 күн бұрын
  • All I can say about this advice: Frontender behavior

    @user-yc4cp7ue2w@user-yc4cp7ue2w17 күн бұрын
  • If you are writing the repository pattern just to abstract away the database "because it might change" tomorrow, stop. That was just a poor example people used to explain it. Real power of Repository pattern comes in abstracting the data retrieval and storage logic. When you write the function in a way which this linkedin post doesn't recommend (which is a bad advice of course), you say that those functions are what people using the repository MUST use and they SHOULD NOT think about internal logic of how repository does it. Why? Because that is internal code and when people are not passing stupid things like predicates, you have freedom to change, optimize and do whatever INSIDE the repository and not affect anything outside of the repository. THAT is the real power it. Now to be fair, there is a compromise solution for the linked in post. Instead of passing a predicate, require the user to pass an object of filters, a class where each public property is optional (unless you require some) and nulled by default. Then in the implementation you use that filter object to decide how YOU want to build that query. In other words, you turn the database into kind of a REST API with requests and responses except way more simpler. You get the best of both worlds. :)

    @arekxv@arekxv7 күн бұрын
  • I actually read the original post, and it was suggesting this as an alternative not as a hard rule. kinda click bait.

    @David-qz1rd@David-qz1rd17 күн бұрын
    • The text was actually omitted from the final edit because it was saying literally nothing. No good case was made for either approach making it pointless.

      @nickchapsas@nickchapsas17 күн бұрын
  • Oh gosh, my argument would simple be about not having clear contract. Having generic contract is path to nowhere in long term. Be explicit, make your code readable.

    @wdolek@wdolek17 күн бұрын
  • Using this method just makes the code harder to read imo, and i prefer to use a wrapper that hides the SQL/expression so i don't have to deal with it on other classes. But you guys make me feel stupid so maybe it's not a problem for you guys.

    @thefattysplace@thefattysplace17 күн бұрын
  • It negates the use of the whole interface, and repo. even if it is just for EF-Core if you do it like that, just use the DBContext. this is just plain stupid. The repository is responsible for querying the DB, it is your infrastructure. By opening it up with a func, you made external methods responsible for querying It is even worse, now in the query you have access to your model, and can set update delete stuff, everywhere, and that should not be the case. Did i mention how stupid i find it to be ? Just in case : THIS IS STUPID

    @Silentsouls@Silentsouls17 күн бұрын
  • I wouldn't use any Repository at all. There is no reason for having a Repository when you're working with EF, since DbSet is repository and DbContext is UnitOfWork. Right now I'm using SQL Server. If I ever want to change to PostgreSQL, all I need to do is just changing the Provider. Also, nobody have ever changed from an ORM like EF to anything else, because it has many advantages which other ORMs don't have.

    @SinaSoltani-tf8zo@SinaSoltani-tf8zo17 күн бұрын
    • I agree, would be a good video discussion topic for Nick

      @dzllz@dzllz17 күн бұрын
    • Exactly

      @T___Brown@T___Brown17 күн бұрын
  • Have you checked your blood pressure before and after reading the Linked-in code advice? 😆

    @trader548@trader54817 күн бұрын
  • You rarely change data providers, so I don't see flaws here. Actually , we used this approach in one if my projects and it worked good. Although few times I needed to traverse the expression tree 😂

    @isnotnull@isnotnull17 күн бұрын
    • It's not about changing them, it's about co-living. For example you have the UserRepository as the main implementation using SQLServer and then the decorated CachedUserRepository that uses the same interface but saves responses to Redis. Good luck implementing this if you're using expressions

      @nickchapsas@nickchapsas17 күн бұрын
    • @@nickchapsas I use a generic repo. If i need to cache something i just use a cache service that cache the result. Something like "Task GetCachedAsync(string key, Func initializer, TimeSpan slidingExpiration);", where the initializer calls the UserRepository and the result is cached.

      @JuanAlvarez-eo2zf@JuanAlvarez-eo2zf17 күн бұрын
  • Vomit... That's what happens when junior devs try get smart.

    @brandonpearman9218@brandonpearman921817 күн бұрын
KZhead