Abp: Use NoTracking for readonly repositories for EF core

Created on 15 Nov 2018  路  9Comments  路  Source: abpframework/abp

abp-framework enhancement high

Most helpful comment

Alternative: All repository get/read methods may have an optional "noTracking" parameter (false by default).

This would get my vote or even extending the AbpRepositoryBase to have additional ReadOnly methods. I don't think there needs to be any magic. Practically the caller would know when they want to get a read only version and they as a side effect would get something that is faster.

All 9 comments

  • Will also improve performance

@hikalkan @natiki
I would like to implement this feature, if nobody else has started

I didn't started.

Currently, IReadOnlyRepository is just an interface and EfCoreRepository implements it in addition to other repository interfaces. So, the implementation is single. I'm not sure how to understand the client is using IReadOnlyRepository interface. We should think on that.

Before fully implementing it, it would be better to share your design idea so I can comment on it.

Thanks for your contribution :)

From quickly looking at the code, I guess it needs to have a separate implementation, since it uses the default repository implementation right now.
However I will investigate more and propose a design idea so we can discuss it before implementing

@hikalkan
My initial idea is to create a separate implementation for readonly repositories. For example:

  • Create ReadonlyRepositoryBase class in the Domain
  • Create EfCoreReadonlyRepository that extends the class above in EfCore.

A simple diagram that I made: https://drive.google.com/open?id=1Lq_H9Vxsg2B0hVX1V1-kmwpo0nFdRyIU

The point here is that these classes will implement only Readonly funcionalities with .AsNoTracking()
Here is an example of the implementation for Count only: https://prnt.sc/lr0ypw
I think you will get the idea for the rest of the functions.

Then we register the Readonly repositories with this implementations instead of the default ones.

This is just the initial idea, don't know if this will break something or if it will be a pain to implement.
What do you think of this?

It's not a big problem for default repositories. It makes the registration progress complicated however it may be acceptable.

The problem is:

When you create a custom repository, you can replace default repositories with your implementation. If you override a base method and change it, it will be valid even if you inject IRepository (instead of IPersonRepository). That's a good advantage and provides consistency between repository interfaces. In fact, there will be a single implementation which exposes multiple interfaces (read only, basic... etc.).

If we separate implementations and want to prevent this behavior, then we need to create 2 implementation for our custom repository which is not good.

The magic implementation may understand the client code is using read only interface and add .AsNoTracking() only in that case. This prevents duplication of implementation to just add a single statement. This magic may be implemented using dynamic proxying/decorator which only applied when dependency is resolved via a readonly interface. And current repository implementation may work in both ways. In this way, we may provide a way of using NoTracking manually even we inject a writable repository interface.

Alternative: All repository get/read methods may have an optional "noTracking" parameter (false by default).

Anyway, I am looking for an elegant solution that is easy to use, compatible with current features :)

@hikalkan
Yeah I think I agree with you actually. This was just my first idea :)
Anyways, I will try to work out something towards your idea. If you come up with something in the meantime, it will be awesome.

You can do it at the context level. Might not make sense for everyone, but for us since we run SQL and can have a RO instance, we can send all the RO to that server, and all the CRUD to the primary.

Note: for our example we need the other context so we can pass over another param in connection string "ApplicationIntent=ReadOnly"

using (var context = new ApplicationDbContext())
{
   context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
   var students = context.Students.ToList();
   var course = context.Courses.FirstOrDefault();
}

Alternative: All repository get/read methods may have an optional "noTracking" parameter (false by default).

This would get my vote or even extending the AbpRepositoryBase to have additional ReadOnly methods. I don't think there needs to be any magic. Practically the caller would know when they want to get a read only version and they as a side effect would get something that is faster.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hitaspdotnet picture hitaspdotnet  路  3Comments

SmallShrimp picture SmallShrimp  路  3Comments

hikalkan picture hikalkan  路  3Comments

hikalkan picture hikalkan  路  3Comments

ugurozturk picture ugurozturk  路  3Comments