How To Use The Specification Design Pattern With EF Core 6

Поделиться
HTML-код
  • Опубликовано: 22 авг 2024
  • НаукаНаука

Комментарии • 205

  • @MilanJovanovicTech
    @MilanJovanovicTech  Год назад +9

    Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
    Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt

  • @PaulSebastianM
    @PaulSebastianM Год назад +25

    What is the point of Specifications, because what I see is that you are replacing 3-5 lines of explicit dbContext querying with multiple classes just to encapsulate a set of expressions applied to dbContext?

    • @muhammadessam7062
      @muhammadessam7062 Год назад +4

      Honestly, when I first heard of the specification pattern it was about applying complex validations on entities, but this approach is also useful and can get even more cleaner with extension methods on the IQueryable class, which will preserve the functional style you are tending to use and make you repos cleaner because it usually scales vertically and gets very messy.

    • @PaulSebastianM
      @PaulSebastianM Год назад +3

      @@muhammadessam7062 i even prefer to just compose functions (railway oriented programming) than extend iqueryable with methods that don't fit in my view with what the function of that iqueryable is.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +5

      You're missing the point because the example is simple. It makes a lot more sense when you have more complex queries, need to include different navigations on different paths etc

    • @ShikaIE
      @ShikaIE Год назад +2

      I thought so too. I was hoping to see some use cases applied on the specification class or the evaluator that shows the point of moving query there which would probably make applying this pattern useful.

    • @PaulSebastianM
      @PaulSebastianM Год назад +3

      @@MilanJovanovicTech I can still do that explicitly with no indirection caused by Specification. The only reason I'd opt for it is code deduplication, if a query is used in more than 1 place and they are using the same underlying data schema/source.

  • @pilotboba
    @pilotboba Год назад +12

    I think part of the point of specifications is that you don't keep adding (OPEN CLOSED) methods to your repo, but accept the specifications which extend your repository. But, you just rewrote your repo methods to call your specifications when I don't think really shows off the power of this pattern.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +5

      You're right on that one! I sometimes focus too much on the concept and implementation that I miss obvious improvements like that.
      it's even how I used the Specification in a real project - GetBySpecification method + many Specification classes

  • @mateuszszczuka2311
    @mateuszszczuka2311 Год назад +7

    Great job! but for me specification pattern:
    - a lot of redundant code
    - the code is unreadable
    Of course this is just my opinion but I'm curious why you recommend this pattern? 👍😎

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I don't recommend anything. I present options. It's up to you to decide if this makes sense for your project or no.
      I have used this pattern with a lot of success on a few projects.
      But if you have a simple app, it's too much.

  • @youssefwael8
    @youssefwael8 Год назад +3

    Everytime I watch your videos, it makes me feel that I have not worked in the right way at all. Keep it up, Milan ❤

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I'm sorry if you feel that way 😅 Make sure to take everything I show with a grain of salt. Never assume I'm 100% correct.

  • @DavidGagne
    @DavidGagne Год назад +8

    Thank you for this video!
    In the Domain-Driven Design Fundamentals course on pluralsight, it says that specifications should belong in the domain model. This is to keep business logic out of persistence layer. I think it is another advantage of using the specification pattern :)

  • @harmroerdink7647
    @harmroerdink7647 Год назад +8

    Good video and interesting pattern. I like that it makes the repository class cleaner but it does feel like entity framework with a lot of extra steps.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +2

      I've used it on a project with many different queries on the same entity, and it shined there.
      But it can certainly be overkill for simple projects.

  • @serb1146
    @serb1146 Год назад +3

    Hi Milan, thanks for your ideas and videos. Imho, using Repository pattern in case of EF is already Over engineering. Anyway, thank you for opening the real example of specification pattern itself.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I don't think it's over engineering in the way that I use it. I do so only on the WRITE side of the application.
      And on the READ side, I use EF or SQL.

  • @StephenOwen
    @StephenOwen Год назад +3

    We use this everywhere at work and it always puzzled me, like, a lot. This is a really good explanation of it, but I still wonder why some folks like this so much.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I think it can be useful when there is a lot of repetitive queries. And it can be overkill in other situations.

    • @StephenOwen
      @StephenOwen Год назад +1

      @@MilanJovanovicTech they explained that is made unit testing much easier, but I could also see the case that unit testing isn't that hard either with in-memory DB either. Thanks for making this video, I will now checkout your newsletter and other things.

  • @SajidAliSoftwareEngineer
    @SajidAliSoftwareEngineer Год назад +3

    awesome Milan, in very short time your channel getting popular because it is due to you hard work and dedication.
    I like your teaching way. it is really understandable.

  • @PaulSebastianM
    @PaulSebastianM Год назад +5

    12:35 did you say simplify? IMO, honestly, it looks like a lot of ceremony replacing something that was much more explicit and clear.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Again, the example is simple. Try to think of the concept in general. I see people missing the forest for the trees often with my videos, because they latch onto the implementation in the example

    • @PaulSebastianM
      @PaulSebastianM Год назад +1

      @@MilanJovanovicTech Don't mean to be rude, don't mistake that for genuine interest in code quality. I'm saying that the examples need to be more practical. This example in my opinion is not practical. Think that so many developers are going to see your videos, love what you do, take you as their teacher, and go in life creating overly-complex systems because they take your examples as good practice and then their projects become unmaintainable messes.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      @@PaulSebastianM You can never stop people from building overly complex systems. Neither should any content creator be to blame for lack of technical reasoning. I'm trying to present concepts I don't see others talk about. I agree that the examples can be more practical, but until I can do this full-time, I don't have enough time to commit to that.

    • @PaulSebastianM
      @PaulSebastianM Год назад

      @@MilanJovanovicTech Totally understand.

  • @cbo4277
    @cbo4277 Год назад +2

    Hello Milan. Thank you very much for this video. Now I combine the generic repository pattern with the specification pattern to make my hexagonal architecture simpler and more maintainable 🙌🏼🙌🏼👏🏻👏🏻

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I'm not a fan of generic repository, but if it works for you that's excellent!

  • @markmbouma
    @markmbouma Год назад +1

    Nice job explaining this somewhat important topic. I’d rather return the task rather than overusing the async/await in your repository.
    Thank you

  • @vitorpascoaI
    @vitorpascoaI Год назад +1

    Really? Yesterday I was looking for this in your channel and didnt find it. And today you posted it. 😂 Thank you for all your videos

  • @mohamed-hassan-
    @mohamed-hassan- 5 месяцев назад +1

    Amazing so many ideas comes to mind just watching this video, nested IncludableQueryable, Projections, etc .. thanks milan.

  • @roefski
    @roefski Год назад +1

    I've never smashed a like button more than I did just now. Great video! 🙂

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I ejoyed saying that too much in the video. And your comment made it even better now 🤣❤️

  • @kabal911
    @kabal911 Год назад +5

    Wrapping EF Core in repository and specification offers nothing really. “Abstracting your code from EF Core” is not a valid argument IMO. In the real world, no one builds a project with EF and then later replaces it with something else. “Tight coupling” - so what?
    Just use EF Core dbContext directly, and have a much simpler code base that is easy to understand and modify.
    If you want to do unit testing, and are unable/unwilling to use an in memory database, you can still create an interface for your dbcontext to implement.
    I did however really enjoy this video. I do like some of these OOP patterns in theory, just not in practice.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      It's just another tool in the toolbox. It made sense on some projects I worked on, tbh. But I agree it's too much for simple use cases

    • @moneymaker7307
      @moneymaker7307 Год назад

      Using a generic repository for common queries quite beneficial. It make absolutely zero sense to litter d code base with where I’d this and that.

    • @kabal911
      @kabal911 Год назад

      @@moneymaker7307 My contention would be that I do not think that a “common query” exists in an application. IMO if 2 pieces of functionality use the “same” query, it’s coincidence.

    • @moneymaker7307
      @moneymaker7307 Год назад

      @@kabal911 yes they most of the time when you write a query to find a resource by Id, then that query exist for all the other resources in your code base.

    • @kabal911
      @kabal911 Год назад

      @@moneymaker7307 I just don’t see the real world value in essentially wrapping “dbContext.Products.Find” in another object.
      I also don’t really consider load by ID a “query”.
      When I am thinking “query”, I am thinking feature A loads a product and includes reviews, while feature B loads a product but includes category.
      Being able to achieve that with a generic repository where I need to use Specification (or similar), again seems needless and offers no actual real world advantage.
      I’ve used repositories (generic and specialized) for most of my career (mostly Java + SpringDataJpa).
      But when it comes to EF Core, I do not believe that abstracting it offers any financial or human advantage. It does not result in safer to change code. It does not result in easier to change/work on code.

  • @sampleuser7727
    @sampleuser7727 Год назад +1

    Great video. This was the one I was looking for after last video.

  • @waleed-alshinawi
    @waleed-alshinawi Год назад +4

    Hello Milan, thanks for the video, great effort and well explained.
    Can you please address the case when I need to use the (ThenInclude) functionality of ef core in this pattern
    again thanks

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +3

      Great question! So you have one option which will work with this implementation.
      Instead of ThenInclude, just include to the level of nesting you need:
      AddInclude(x => x.Navigation1.NestedNavigation)
      You can also write a string include implementation, it works with EF. But I didn't show it in the video.
      You need a List, and apply them the same way with Aggregate.

    • @TeamFoltingChannel
      @TeamFoltingChannel Год назад +1

      @@MilanJovanovicTech nesting does not work.

  • @muradhossen2347
    @muradhossen2347 Год назад +1

    I always love your videos. Always simple and important.

  • @Qrzychu92
    @Qrzychu92 Год назад +11

    I always wonder when the additional layer of abstraction stops giving you benefits. I really like an approach, where you work with IQueryable the whole time. You write extension methods like BuildProduct to include all the properties, or to group together some common operations. If there is a lot of properties, you can make a QueryDto and extension method that applies filters, orderBy etc onto the IQeuryable. You end up with something akin to builder pattern, which is understandable even by people who don't know what a builder pattern is :)
    I like this approch, because there is always some edge case that you need to account for (like to SplitQuery in your example, possibly used only in this one endpoint, but now you have a whole class to represent that case). Having the IQueryable accessible gives you so much flexibillity, and in practice saves a lot of DB trips. How many times did you use your repository to query a list just to do FirstOrDefaut() or Any() on it?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      How many times did I query for a list to return FirstOrDefault or Any? Never.
      Okay, maybe when I first worked with EF 5 years ago. 😅
      I also like your approach with extension methods, great option

    • @PaulSebastianM
      @PaulSebastianM Год назад +4

      Too much unneeded abstraction IMO. The pitfalls of OOP. I prefer a simple functional approach with clear simple code that doesn't make me jump around the codebase to understand the entire logic behind a request handler.

    • @NoGamer256
      @NoGamer256 Год назад +3

      @@PaulSebastianM It is not unneeded abstraction. It is just wrong abstraction. What's the benefit of doing this instead of using extension methods on DbSet objects? Except for being fancy.

    • @NoGamer256
      @NoGamer256 Год назад +1

      I think it only makes sense when you can separate your implementation from your interface. When you are going to use IQueryable you are just commiting to c# expression api and EF Core and there is nothing wrong with that approach. In this example we got complexity of custom soultion but no benefit as we are still tied to c# Expression api by our base class.

    • @Qrzychu92
      @Qrzychu92 Год назад +2

      @@NoGamer256 I agree, but IQueryable and expression API is the way to go in C#.
      On the other hand, if you return that IQueryable from a repository, in principle, you can replace EF Core with anything: nHibernate also return IQueryable, you can write your own IQueryable to SQL converter and use Dapper or just plain old ADO. Yes, probably some behaviours will change.
      IQueryable is the abstraction here. Quite low level, but it has nothing to do with EF Core, it even lives in System.LINQ. It was designed to abstract persistent data sources, just like IEnumerable abstracts in memory collections.

  • @SoyUnEmilio
    @SoyUnEmilio Год назад +1

    Hi, I like your Channel, I'm using specification at the domain level to be able to use them both in the domain validations both in the queries. You are using It in the infrastructure layer but I like your method!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Thanks! Nothing is stopping us from moving it to Domain layer

  • @iaswaiss
    @iaswaiss Год назад +2

    great video, I would've loved if you included another powerful use case for specification design pattern where you can use it for validations.
    you can use the expression to query the db. also, you can use expression compile method to validate your input.

  • @cleitoncarlospereira2006
    @cleitoncarlospereira2006 8 месяцев назад +1

    Hi Milan, I look forward to part 2 with a pagination implementation with this pattern.

    • @MilanJovanovicTech
      @MilanJovanovicTech  8 месяцев назад

      Should I do that? 🤔

    • @cleitoncarlospereira2006
      @cleitoncarlospereira2006 8 месяцев назад

      @@MilanJovanovicTech Hi Milan, you promised to do this in a previous answer to this question in this video, take a look, now it's up to you to decide whether to do it.

  • @iKaVaPerv
    @iKaVaPerv Год назад +1

    I have a feeling that you saw that the Count was 0 at 18:55 😅. Great video once again!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I left a popup that I made a mistake when implementing the Includes :(
      Forgot to assign result of Aggregate back to the queryable

  • @alexkovanev1425
    @alexkovanev1425 Год назад +3

    I remember those times when EF didn't exist and queries were being written in the code. Nowadays this is a huge pain for monoliths having all this stuff super hard to rework. Furthermore, not queries themselves but a wrapped up infrastructure is a monster.
    While relying on a specific implementation, like EF, one day in the future that will be a new pain again. I'm not even going to emphasize the EF issues, specific to the topic, like collecting related data for related tables (properties), but lets assume you have to migrate to mongodb one day. No EF anymore. The approach based on expressions will not just smoothly be working.
    What I'm trying to say. I respect the author of this video, you are doing a truly great job. But I would carefully think about if this approach is really what I need. If you Milan find time to implement the same stuff for a nosql database I bet you will improve your solution.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +4

      I don't think EF is going anywhere to be honest. And if you're going from SQL to MongoDb, you'll have much bigger problems than rewriting queries...

  • @nourhannehigazy4339
    @nourhannehigazy4339 Год назад +1

    Great video, thanks a million for sharing it with us.

  • @talkathiriify
    @talkathiriify Год назад +1

    Very valuable and useful video.
    Thank you for sharing.

  • @Petoj87
    @Petoj87 Год назад +1

    Correct me if I'm wrong but there is a Predicate

  • @gibran800
    @gibran800 Год назад +2

    Great video, thanks for sharing! I just have a quick question: why should we use Expression instead of just Func? Is it because EF uses expressions to convert LINQ to SQL?

    • @muhammadessam7062
      @muhammadessam7062 Год назад +1

      Yes that's the reason, in the end, you saw that the criteria you send to the specification object is used on calling the queryable object, so that's why.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Because that's what EF uses, as the other comment said

  • @User.Youtube.1027
    @User.Youtube.1027 7 месяцев назад

    // Specification Minimal Version
    // Advantages
    // * can add multiple criteria
    // * can apply multiple ordering
    public class XSpecification
    where T : class
    {
    public List CriteriaExpressions { get; } = new();
    public List IncludeExpressions { get; } = new();
    public bool IsSplitQuery { get; set; }
    public bool AsNoTracking { get; set; }
    public Func? OrderFunction { get; set; }
    public void AddCriteria(Expression expression) => CriteriaExpressions.Add(expression);
    public void AddInclude(Expression expression) => IncludeExpressions.Add(expression);
    public IQueryable Evaluate(IQueryable inputQuery)
    {
    IQueryable query = inputQuery;
    CriteriaExpressions.Aggregate(query, (current, expression) => query = current.Where(expression));
    IncludeExpressions.Aggregate(query, (current, expression) => query = current.Include(expression));
    if (IsSplitQuery)
    {
    query = query.AsSplitQuery();
    }
    if (AsNoTracking)
    {
    query = query.AsNoTracking();
    }
    if (OrderFunction is not null)
    {
    query = OrderFunction.Invoke(query);
    }
    return query;
    }
    }
    // usage
    var name = "john";
    var _context = new ApplicationDbContext();
    var inputQuery = from x in _context.Users
    select x;
    var xspec = new XSpecification();
    xspec.AddCriteria(UserExpression.IsEnabledExpression);
    xspec.AddCriteria(x => (!string.IsNullOrEmpty(x.UserName) && x.UserName.Contains(name))
    || (!string.IsNullOrEmpty(x.FullName) && x.FullName.Contains(name)));
    xspec.AddInclude(x => x.Branches);
    xspec.AsNoTracking = true;
    xspec.OrderFunction = (query) => query.OrderBy(x => x.UserName).ThenByDescending(x => x.FullName);
    var resultQuery = xspec.Evaluate(inputQuery);
    // Console.WriteLine(resultQuery.ToQueryString());
    // Console.WriteLine();
    foreach (var u in resultQuery)
    {
    Console.WriteLine($"{u.UserName}, {u.Email}");
    foreach (var b in u.Branches)
    Console.WriteLine($" - {b.Name}");
    Console.WriteLine();
    }

  • @iliashterev38
    @iliashterev38 Год назад +1

    For second time - Greetings from Bulgaria. Thank you for your time and efforts to create and share those nice tutorials.
    Well, this time you did not win me. I fail to understand. From one class with 20 lines of code you made 5 classes with 50 lines of code !!!??? And one single change will require going through layers of classes.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Makes sense, but imagine having to reuse these specifications in quite a few places - then there's more value to it. Check out this repo also: github.com/ardalis/Specification

  • @charliebrownsabstractmind
    @charliebrownsabstractmind Год назад +1

    Nice explanation bud. Subbed!

  • @volodymyrliashenko1024
    @volodymyrliashenko1024 Год назад +4

    Hello Milan. Thanks for the video.
    It looks like I missed what was the reason to implement Specification Pattern here.
    In generally Specification pattern here is a wrapper on IQueryable which allows you do not depend on EF Core and implement Queries on business logic level and potentially you can use the same specifications for Mongo database (as an example).
    But what problem was solved in this case?
    I'm asking because I've heard about that approach many years ago, but didn't find a reason to apply into my project because no understanding why this is better then what you had before or bettern then extension methods.
    Thanks!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      This implementation is EF specific. Perhaps the example is too simple for the pattern to make sense. Try to think of more complex queries with a lot of includes in different methods.

    • @volodymyrliashenko1024
      @volodymyrliashenko1024 Год назад +1

      @@MilanJovanovicTech But what the issue having that implementation in the method of repository class? I don't think it can be bigger then 100 lines of code.
      I'm using projections and I think most of the case you need to use projections for optimal queries. And once you use it you don't need to write .Include(). EF will do that automatically for you.
      Projections itself I have in DTO clases responsible for projections. So, the only code I have in repository pattern is Where conditions.

    • @volodymyrliashenko1024
      @volodymyrliashenko1024 Год назад +1

      @@MilanJovanovicTech Please understand my comments as a knowledge sharing. This is the only goal I have. Thanks

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@volodymyrliashenko1024 No worries, I never mind my ideas being challenged 😁
      I use repositories on the read side, so it makes sense to leave the Includes.
      If I want to use projections, that's going to be on the read side where I return data from the API. In which case the Specification pattern probably makes little sense. Agreed.

  • @Daanik8
    @Daanik8 Год назад +1

    Thank you for the video!

  • @Yehuda-Shor
    @Yehuda-Shor 8 месяцев назад

    Hi Milan, first of all thank you very much for the rare content you bring us.
    Secondly, I saw that a lot of people said that it should be done in the Domain and I also think so, the question is what to do with functions that belong only to EF such as Include, how can you access them in the DOMAIN and still keep the domain clean.

    • @MilanJovanovicTech
      @MilanJovanovicTech  7 месяцев назад

      You can't place this in the domain without leaking implementation details
      Maybe just place the WHERE filter in the specification, that can freely go in the domain

  • @AnotherFancyUser
    @AnotherFancyUser Год назад +1

    By instantiating a concretion to your low level module (which is the repository) wouldn't you create a tight couple between your now high level module (repo) to your Specifications? Even if you implement a generic interface ISpecification and then have them ready in your container, what would you do if you have to change your repo to not use any specification?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      That's such a low level decision. You'd decide at the start what path you want to take (think carefully). And commit to it. If you decide on a whim to change implementations, that says a lot...

    • @AnotherFancyUser
      @AnotherFancyUser Год назад +1

      @@MilanJovanovicTech Hey Milan how are you? Glad you decide to respond.
      Even though principles are there if you want to follow them, I would said D in SOLID is very important (well... all of them), if in 3 years the company want to change db providers that don't work well with LINQ to SQL then this approach wont work as your Specifications uses LINQ as you are using the concretions directly (which you tie your I/GaterhingRepository to marry db providers that uses LINQ to SQL), but usually EF Core has a lot of NuGet packages to work with those other engines anyway.
      In regards changing implementations on a whim I would say... welcome to IT? This happens a lot and the people from the top decides to make this big chances surely for financial reasons more than other.
      Anyway, great content!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      @@AnotherFancyUser If you change from SQL to something else (MongoDb) you're screwed either way. You have to rewrite everything.
      If you change from SQL Server to PostgreSQL, you most likely won't have any issues. Just switch EF packages.
      What other use case do you have in mind? 🤔

  • @Dragonet17
    @Dragonet17 Год назад +1

    Very good video I want more ( mostly peerformance videos ) :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I have a few performance videos coming! 😁

    • @Dragonet17
      @Dragonet17 Год назад +1

      @@MilanJovanovicTech ❤

    • @Dragonet17
      @Dragonet17 Год назад

      @@MilanJovanovicTech I thout that you can combain this topic with custom projecton in Select method, what do you think ? It would be greate

  • @Kasiux
    @Kasiux 3 месяца назад +1

    Can we use this pattern for something like dapper too? With manual mapping 1-m it seems a little unhandy, doesn't it?

    • @MilanJovanovicTech
      @MilanJovanovicTech  3 месяца назад

      I doubt you'll be able to make it work as easily as with EF Core

  • @cyrildouglas9262
    @cyrildouglas9262 Год назад +1

    Great video, thanks as always, it would be great if you do a video specifically on cancelation token.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Thanka Cyril. I have that topic in the pipeline, I'll come through with the video sooner or later.

    • @cyrildouglas9262
      @cyrildouglas9262 Год назад

      @@MilanJovanovicTech Really appreciate, thanks again.

  • @silvertek
    @silvertek Год назад +2

    Great video and explanation. I started using Steve "Ardalis" Smith's implementation of the specification pattern a while back, and this is a great breakdown of how to implement it yourself. Love the channel, keep up the great work!
    What's your opinion on having a more generic Repo method that takes and applies a specification as a param, rather than creating huge numbers of custom repo methods that implement specific specifications?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I would also suggest using Steve's library, since it's awesome and more robust than what I showed.
      I think that's a nice option for sure, but I prefer having well named repository methods. Nothing wrong with either approach. Just a personal preference.

  • @liva236muzika
    @liva236muzika 3 месяца назад

    I am fed up with abstractions. In my latest project I got rid of repository pattern altogether, just injecting DbContext directly and doing EF Core Linq queries from my handlers. What Milan shows goes in the opposite direction - he has not one but two abstractions: specification pattern and then ageneric repository on top of it. Not sure where the value is though. Nice video though, informative.

    • @MilanJovanovicTech
      @MilanJovanovicTech  3 месяца назад +1

      I think all abstractions make sense in a given context. It's up to you to figure it if this makes sense to use or not. I don't use Specifications all too often, but I find it an interesting pattern.

  • @mostafasalaheldin812
    @mostafasalaheldin812 7 месяцев назад +1

    Thanks, bro what if I need more than one condition in the where clause, for example, I need Employee where the name is "Patrick" and IsPresent. How to implement that here

    • @MilanJovanovicTech
      @MilanJovanovicTech  7 месяцев назад

      Pass the conditions as arguments to the specification

  • @T___Brown
    @T___Brown Год назад +1

    Thanks for teaching this. Not sure its for me though.

  • @charlesschneiderp
    @charlesschneiderp 11 месяцев назад

    Really good! Thank you!

  • @NoGamer256
    @NoGamer256 Год назад +3

    I don't think that's a good idea. It is ilusion of abstraction. Yes your specification class doesn't depend on Expressions but wait your base class does. You couple your specification defining objects with c# expression api cause you know that you are going to use EF Core in your repository. In proper implementation your specification object should be only translated by separate module specific to your infrastructure. So when you use EF Core you will translate specification to expression, when you use raw sql you will translate it to raw sql and so on. Now does it makes sense and is it easy? No. Propably very hard. I would just use raw db context instead with correctly named extension methods to encapsulate query logic.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Well, this is a Specification + EF implementation. What did you expect?

  • @TheKillermob13
    @TheKillermob13 Год назад +1

    if you delete all the async/await from your code...how it will affect the app ?

  • @cleitoncarlospereira2006
    @cleitoncarlospereira2006 Год назад +1

    great content, congrats! Could you increment this example with the pagination part using specification?

  • @antondoit
    @antondoit Год назад +1

    Very nice😀

  • @F2H16
    @F2H16 11 месяцев назад +1

    Hi Milan nice video. One question though which stop me from using it, is it possible to inject a specification via dependency injection? I would like to to inject a specification from outside. Let's say, in case of TDD, I would like to use a mocked or stubbed version of that specification, can I do that ?

    • @MilanJovanovicTech
      @MilanJovanovicTech  11 месяцев назад

      Doesn't make a lot of sense when specifications need arguments to create the parameters. But you can always inject them from the method arguments.

    • @F2H16
      @F2H16 11 месяцев назад

      I couldn't pass my point around. Sorry, my query was, if I can inject the, i.e... gatherby..specification to a mediatr command or query? I don't want to new that up inside a mediatr handler.

  • @HubertoKusters
    @HubertoKusters Год назад +1

    The specifications are now defined in the persistence layer. Should you not define them in Domain or Application, because how and what we retrieve from the database is Domain specific, is it not?

  • @ricardopaulinonascimento
    @ricardopaulinonascimento Год назад +1

    good job!

  • @antonmartyniuk
    @antonmartyniuk Год назад +1

    Cool. I would also creare an AndSpecification and OrSpecification to combine specifications

  • @dionismendanha4649
    @dionismendanha4649 Год назад +1

    Great video made me have several ideas on how to improve my projects. A doubt that came to my mind is it a good idea to pass the specification as a parameter to the getbyid and getall functions? to have a default behavior and a specific one as I need?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      You can expose a specification parameter and let the consumer decide what to query, for sure

    • @dionismendanha4649
      @dionismendanha4649 Год назад

      @@MilanJovanovicTech thanks

  • @robertraboud1466
    @robertraboud1466 Год назад +1

    how does this work with secondary level includes ie .ThenInclude()?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Not that well 😅 Use Ardalis.Specification library for a better implementation

  • @Tamer_Ali
    @Tamer_Ali Год назад +1

    Hi Milan, could you please talk about Hi/Lo algorithm? and how to apply it in EF Core?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I've rarely seen it used in practice. But sure, I'll add it to my list of topics. 😁

  • @arianhaghani3452
    @arianhaghani3452 Год назад +1

    thank you for amazing video , how can access this project ?is exit public repository for clone?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I share it on my Patreon page: www.patreon.com/milanjovanovic

  • @lampbreaker
    @lampbreaker 11 месяцев назад +1

    I unsmashed my like when I look for the source code I was asked to pay

  • @user-fj6js6px6f
    @user-fj6js6px6f Год назад +1

    Nice video, but what is solution if i want to use ThenInclude method on one to many?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      You can use the existing solution, if it's not a list:
      AddInclude(x => x.Navigation1.Navigation2)
      Or implement the includes using strings instead of expressions
      Also, check out ardalis/Specification for a better implementation

  • @gauravsingh-qt2zo
    @gauravsingh-qt2zo Год назад +1

    Can anyone explain when to use dynamic and object keyword????

  • @ahsanali3857
    @ahsanali3857 8 месяцев назад +1

    how can i add projection in it?

  • @mateherbert3830
    @mateherbert3830 Год назад +1

    Hi Milan! Firstly, thanks for the great video! As I read through the comments below, you and others mentioned that instead of creating specific repository methods like GetBySomethingWithXAndY writing only a GetBySpecification method would be OCP complient and more elegant. This implementation would imply that the Specification class should be moved to the Application layer so that the derived classes of the Specification could be instantiated and given to the proper GetBySpecification methods in the CQRS IRequestHandler implementations. However, this would mean that through Specification EF-specific details would be placed (eg IsSplitQuery) in the Application layer. Isn't that a break of the Clean Code Architecture? Or is there something that I misunderstood? What is your opinion?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      That's correct. You would be introducing EF specific details into Application/Domain layer (not directly, but as you mentioned).
      Are you okay with that tradeoff?

  • @alessandromenchini439
    @alessandromenchini439 Год назад +1

    How would you handle the case where a certain entity's repository (with its includes) needs to be reused in another repository (to not duplicate the same includes)?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Inject and use that repository?

    • @alessandromenchini439
      @alessandromenchini439 Год назад

      @@MilanJovanovicTech Excuse me, but I don't understand how the following case could be handled: suppose I have Customer with his contact details. The specification could give me a way to get the Customers with the inclusion of the contact details. But when do I have to reuse customers with contact details within an Order specification? Suppose I need an Order with Customer details including contact details. It seems to me that I should still reply to the include contact details also in the order. Maybe I'm missing something, or I don't fully understand.

  • @morganishere123
    @morganishere123 Год назад +1

    Really nice video, I followed along in my own project and got great results, simplifying my repository logic. Could this approach by used with projection as well, or do you prefer to project in service layer after data has been returned from the database?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      It's going to be more reusable if you don't couple projection with the Specification

    • @grumpydeveloper69
      @grumpydeveloper69 Год назад

      @@MilanJovanovicTech Your example only returned FirstOrDefault but for returning a list of Companies where you only need Id and Name it would be a shame not to use the projection option in EF to elect only those two fieldsfrom the database.
      Concrete example: A customer picker where you want to select 1 customer but allow selection based on company name and the user can start typing until the list of (take 20) contains the customer. Here it would ba a shame to return an entire top 25 customers when You are only interested in the name and id

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@grumpydeveloper69 Well you still have an IQueryable, and you can chain a Select on it before FirstOrDefault to materialize what you need?

  • @seanvogel8067
    @seanvogel8067 Год назад +1

    In your specification evaluator you put else if - Is that a bug? It seems like you can only have a where clause or an order by.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      You can have both, they are applied independently

    • @seanvogel8067
      @seanvogel8067 Год назад +1

      @@MilanJovanovicTech , Sorry! I see now. I looked at the code 3x and managed to missread it every time. Thanks for making the videos 😎

  • @ucretsiztakipci6612
    @ucretsiztakipci6612 2 месяца назад

    What are the biggest benefits of using it?

  • @NabeelFarooqui
    @NabeelFarooqui 5 месяцев назад

    Does this only work if both mt specification and my dbcontext use the domain object? (TEntity).
    What if i want different models representing my db tables which are separate from my domain objects. Where do i map these two different models?

    • @MilanJovanovicTech
      @MilanJovanovicTech  5 месяцев назад

      Then you have to translate the specification somehow to create an expression of your DB model

  • @phugia963
    @phugia963 Год назад +1

    this is great, but how do you handle thenInclude in this design?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      A nested include will do, or include by string. You can check out ardalis/Specification for a full library

    • @vishoek69
      @vishoek69 Год назад

      For ThenInclude add the following to Specification.cs:
      Specification.cs
      public List IncludeStrings { get; } = new List();
      protected virtual void AddInclude(string includeString)
      {
      IncludeStrings.Add(includeString);
      }
      And here an example of a specifications function with ThenInclude for the Gathering entitie:
      using Gatherly.Domain.Entities;
      namespace Gatherly.Persistence.Specifications;
      internal class GatheringByNameSpecification : Specification
      {
      public GatheringByNameSpecification(string name)
      : base(gathering => string.IsNullOrEmpty(name) ||
      gathering.Name.Contains(name))
      {
      AddInclude(gathering => gathering.Attendees);
      AddInclude($"{nameof(gathering.Attendees)}.{nameof(Attendees.Skills)}");
      }
      }
      And in the SpecificationEvaluator.cs add:
      queryable = specification.IncludeStrings.Aggregate(queryable,
      (current, include) => current.Include(include));
      * => this part is the ThenInclude: AddInclude($"{nameof(gathering.Attendees)}.{nameof(Attendees.Skills)}");
      Here we assume there is a table with Attendee skills

  • @DimonSmart
    @DimonSmart Год назад +1

    I'm using specification pattern same way as described in a video. And recently I faced with a problem of using Left Outer Join to access to included entities. Do you know how to use specification patter along with left outer join? (All information a googled provide only sql style examples)

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +2

      There's always a situation where these patterns fall on their face. I would check if Ardalis.Specification package has a solution for that. Otherwise, just implement it with EF Core

    • @DimonSmart
      @DimonSmart Год назад

      @@MilanJovanovicTech I solved my problem with left outer join locally by adding to my table two "MyRefId" and "MyRefIdOriginal" columns. I filled both of them on created. And use one for history purpose and one fo Left Outer Join simulation by setting cascade set null for this field. So now I can just filter out rows by RefId is null. This allows me to use specification pattern. Any way if any information about left outer join with specification - please let us know.

    • @fredyboy162
      @fredyboy162 8 месяцев назад

      @@MilanJovanovicTech Hi Milan, do you use Ardalis.Specification inside your projects ?

  • @AhmedHassan-gk3tx
    @AhmedHassan-gk3tx 6 месяцев назад

    in which case we will need to set IsSplitQuery as false ?

  • @gauravsingh-qt2zo
    @gauravsingh-qt2zo Год назад +1

    Razor pages vs aspx???? Which one is better???

    • @morganishere123
      @morganishere123 Год назад

      aspx is very dated nowadays, burdening many projects that are using .NET Framework from moving to .NET Core/5+. Razor pages are the more modern approach, utilizing a similar syntax

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I can't say, didn't work MVC in ages

  • @velkumars26
    @velkumars26 Год назад +1

    @Milan Jovanović, how to add AsNoTracking() and ThenInclude

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      AsNoTracking the same approach as SplitQuery. Add a flag, and if true, apply AsNoTracking.
      For ThenInclude, you can do AddInclude(x => x.Navigation1.Navigation2)
      Or, you can add support for string includes

    • @vishoek69
      @vishoek69 Год назад

      For ThenInclude add the following to Specification.cs:
      Specification.cs
      public List IncludeStrings { get; } = new List();
      protected virtual void AddInclude(string includeString)
      {
      IncludeStrings.Add(includeString);
      }
      And here an example of a specifications function with ThenInclude for the Gathering entitie:
      using Gatherly.Domain.Entities;
      namespace Gatherly.Persistence.Specifications;
      internal class GatheringByNameSpecification : Specification
      {
      public GatheringByNameSpecification(string name)
      : base(gathering => string.IsNullOrEmpty(name) ||
      gathering.Name.Contains(name))
      {
      AddInclude(gathering => gathering.Attendees);
      AddInclude($"{nameof(gathering.Attendees)}.{nameof(Attendees.Skills)}");
      }
      }
      And in the SpecificationEvaluator.cs add:
      queryable = specification.IncludeStrings.Aggregate(queryable,
      (current, include) => current.Include(include));
      * => this part is the ThenInclude: AddInclude($"{nameof(gathering.Attendees)}.{nameof(Attendees.Skills)}");
      Here we assume there is a table with Attendee skills

    • @velkumars26
      @velkumars26 Год назад

      @@MilanJovanovicTech, Thank you some much. Let me check, But I'm not fan of using strings in Include.
      All your video's are very much useful. Keep rocking 🙂

    • @user-xn4ko4tn9s
      @user-xn4ko4tn9s Год назад

      @@vishoek69 String include approach becomes hardly supported with rich domain model and inheritance. Maybe it can be accomplished with some fluent includes builder

  • @zaid14zaid14
    @zaid14zaid14 Год назад +2

    How to implement it with thenInclude?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Instead of ThenInclude, just include to the level of nesting you need:
      AddInclude(x => x.Navigation1.NestedNavigation)
      You can also write a string include implementation, it works with EF. But I didn't show it in the video.
      You need a List, and apply them the same way with Aggregate.

    • @zaid14zaid14
      @zaid14zaid14 Год назад +1

      @@MilanJovanovicTech
      Thanks for the reply.
      The problem with level nesting is when x.Navigation1 is a list you can’t go to x.Navigation2.
      But I might take look at string list implementation.

    • @vishoek69
      @vishoek69 Год назад

      For ThenInclude add the following to Specification.cs:
      Specification.cs
      public List IncludeStrings { get; } = new List();
      protected virtual void AddInclude(string includeString)
      {
      IncludeStrings.Add(includeString);
      }
      And here an example of a specifications function with ThenInclude for the Gathering entitie:
      using Gatherly.Domain.Entities;
      namespace Gatherly.Persistence.Specifications;
      internal class GatheringByNameSpecification : Specification
      {
      public GatheringByNameSpecification(string name)
      : base(gathering => string.IsNullOrEmpty(name) ||
      gathering.Name.Contains(name))
      {
      AddInclude(gathering => gathering.Attendees);
      AddInclude($"{nameof(gathering.Attendees)}.{nameof(Attendees.Skills)}");
      }
      }
      * => this part is the ThenInclude: AddInclude($"{nameof(gathering.Attendees)}.{nameof(Attendees.Skills)}");
      Here we assume there is a table with Attendee skills

    • @sagnit.
      @sagnit. Год назад

      @@MilanJovanovicTech How about using the already mentioned "IncludeStings" implemention and some additional methods in the Specification.cs
      protected void AddInclude(Expression includeExpression, Expression thenIncludeExpression) where TSubEntity : BaseEntity
      {
      var path = GetExpressionMemberPath(includeExpression.Body).Select(e => e.Member.Name).ToList();
      path.AddRange(GetExpressionMemberPath(thenIncludeExpression.Body).Select(e => e.Member.Name));
      string queryString = string.Join('.', path);
      this.AddInclude(queryString);
      }
      private IEnumerable GetExpressionMemberPath(Expression expression)
      {
      if (expression is MemberExpression memberExpression)
      {
      foreach (var item in GetExpressionMemberPath(memberExpression.Expression!))
      {
      yield return item;
      }
      yield return memberExpression;
      }
      }
      Usage:
      this.AddInclude(x=> x.Navigation1, navigation1 => navigation1.Navigation2);
      It will create the required string "s.Navigation1.Navigation2" for you in the background, where the nested Navigation1 is an enumerable.

  • @ishfaqbabar1192
    @ishfaqbabar1192 Год назад +1

    Have you uploaded this project to github? please share a link

  • @kenlnetherland
    @kenlnetherland Год назад +4

    Entity Framework and anonymous types totally do away with the need to use ridiculous design patterns that result in unneccessary code. Looking at you "repository" and "unit of work". Why add additional code that just bloats the footprint, increases the learning curve for other, and adds unnecessary complexity. Design patterns here are not needed! The DBContext can easily be added as a service and mocked for unit tests. EF itself can handle multiple back end databases, as well as other providers. Just stop with the patterns! The best code is the code you don't need to write. Design pattern zealots, please tell me where you work, so I don't apply there, and have to unravel the design-pattern mess that "only you" think is brilliant.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Show me a good example of mocking the DbContext for unit tests?

    • @kenlnetherland
      @kenlnetherland Год назад

      var storeContext = new Mock();
      var orders = new List();
      var ordersCount = 0;
      storeContext.Setup(m => m.SaveChanges()).Returns(() =>
      {
      count += Math.Max(ordersCount, orders.Count);
      return count;
      });
      mocks.storeContext = storeContext.Object;
      devOpsContext.SetupGet(m => m.Orders).Returns(() =>
      {
      // add initial orders
      }

  • @zo1dberg
    @zo1dberg 5 месяцев назад

    I dunno. Seems like another level of indirection to me. Interesting patterh, though.

    • @MilanJovanovicTech
      @MilanJovanovicTech  5 месяцев назад +1

      Interesting, not for every project though

    • @zo1dberg
      @zo1dberg 5 месяцев назад

      @@MilanJovanovicTech Good for RDD as well (Resume Driven Development)

  • @DavidSmith-ef4eh
    @DavidSmith-ef4eh 16 дней назад

    would it make sense to have a generic specification that gets passed as an argument to the repository? this way you could have powerful findOne/findMany repository methods, that do not directly rely on entity core and would allow for future refactors to use something else

    • @MilanJovanovicTech
      @MilanJovanovicTech  16 дней назад

      This becomes pretty complex pretty soon (but yes you can do it)

  • @PaulSebastianM
    @PaulSebastianM Год назад

    GetQuery() sounds more like an ApplySpecificationToQuery...