-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for EFCore AsNoTracking() Linq extension. #57
Comments
Hello! Any thoughts on: My implementation thought process: I very much like your libraries and you have a very similar development thought process to my own in your libraries. I have my own 'Repository' implementation which I have been refactoring for a while, recently I added many of the Fluxera libraries to my own projects. The only reason why I created my own implementation at this point was so that I could have more verbose methods, projections, pagination via Gridify rather than Code Snippets on implementation My The owner of the Repo made had some opinions on how he made projections in his library which were a major limitation for me. So I forked it and have been expanding the feature set. Below are snippets of how I implemented This is taken from the equivalent of your /// <inheritdoc />
async Task<PagedResponse<TProjected>> IAeonicCanFind<TEntity>.FindManyAsync<TProjected>(
EfTrackingOptions asNoTracking,
IPaginatedRequest filter,
Expression<Func<TEntity, TProjected>> projectExpression,
CancellationToken cancellationToken)
{
return await this._innerRepository.FindManyAsync(asNoTracking, filter, projectExpression, cancellationToken);
}
/// <inheritdoc />
async Task<PagedResponse<TEntity>> IAeonicCanFind<TEntity>.FindManyAsync(
EfTrackingOptions asNoTracking,
IPaginatedRequest filter,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> includeExpression,
CancellationToken cancellationToken)
{
return await this._innerRepository.FindManyAsync(asNoTracking, filter, includeExpression, cancellationToken);
}
/// <inheritdoc />
async Task<PagedResponse<TEntity>> IAeonicCanFind<TEntity>.FindManyAsync(
EfTrackingOptions asNoTracking,
IPaginatedRequest filter,
CancellationToken cancellationToken)
{
return await this._innerRepository.FindManyAsync(asNoTracking, filter, cancellationToken);
}
/// <inheritdoc />
IEnumerable<TProjected> IAeonicCanFind<TEntity>.FindMany<TProjected>(
EfTrackingOptions asNoTracking,
Expression<Func<TEntity, TProjected>> projectExpression)
{
return this._innerRepository.FindMany(asNoTracking, projectExpression);
}
/// <inheritdoc />
IEnumerable<TProjected> IAeonicCanFind<TEntity>.FindMany<TProjected>(
EfTrackingOptions asNoTracking,
Expression<Func<TEntity, bool>> whereExpression,
Expression<Func<TEntity, TProjected>> projectExpression)
{
return this._innerRepository.FindMany(asNoTracking, whereExpression, projectExpression);
} I have an enum called /// <summary>
/// Sets EntityFramework queries to
/// <see cref="AsNoTracking" />, <see cref="AsNoTrackingWithIdentityResolution"/> or <see cref="WithTracking" />
/// </summary>
public enum EfTrackingOptions
{
/// <summary>
/// Disables EfCore change tracking
/// <remarks>The fastest option, however I your query returns 4 books and their authors,
/// you’ll get 8 instances in total even if 2 books have the same author. </remarks>
/// </summary>
AsNoTracking,
/// <summary>
/// Enables EfCore Change Tracking
/// <remarks>Slowest option - however entities utilize full change tracking</remarks>
/// </summary>
WithTracking,
/// <summary>
/// Disables EfCore change tracking - Will still perform identity resolution
/// to eliminate duplicate instances of objects.
/// <remarks><see cref="AsNoTrackingWithIdentityResolution"/> is a little slower than
/// the fastest option using <see cref="AsNoTracking"/></remarks>
/// </summary>
AsNoTrackingWithIdentityResolution
} Then I have an extension class similar to your /// <summary>
/// Applies change tracking to the <see cref="IQueryable{T}"/>
/// </summary>
/// <param name="queryable">The target <see cref="IQueryable{T}"/></param>
/// <param name="trackingOptions">Tracking options.
/// Default: <see cref="EfTrackingOptions.WithTracking"/></param>
/// <typeparam name="T"></typeparam>
/// <returns>Returns the <see cref="IQueryable{T}"/> with configured change tracking
/// based on <see cref="EfTrackingOptions"/></returns>
public static IQueryable<T> Apply<T>(
this IQueryable<T> queryable,
EfTrackingOptions? trackingOptions)
where T : class
{
Guard.Against.Null(queryable, nameof(queryable));
if (trackingOptions == null)
{
return queryable.AsTracking();
}
return _configureChangeTracking();
IQueryable<T> _configureChangeTracking()
{
return trackingOptions switch
{
EfTrackingOptions.WithTracking => queryable.AsTracking(),
EfTrackingOptions.AsNoTrackingWithIdentityResolution => queryable.AsNoTrackingWithIdentityResolution(),
_ => queryable.AsNoTracking()
};
} Then I can do this (this snippet is from the equivalent of /// <inheritdoc />
protected sealed override IAsyncEnumerable<TProjected> FindManyAsync<TProjected>(
EfTrackingOptions asNoTracking,
IAeonicSpecification<TEntity> specification,
Expression<Func<TEntity, TProjected>> projectExpression,
CancellationToken cancellationToken = default)
{
var queryable = this.Queryable.Apply(asNoTracking)
.Apply(specification)
.ToProjection(projectExpression);
return this.ToListAsync(queryable, cancellationToken);
}
/// <inheritdoc />
protected sealed override IAsyncEnumerable<TEntity> FindManyAsync(
EfTrackingOptions asNoTracking,
CancellationToken cancellationToken = default)
{
var queryable = this.Queryable.Apply(asNoTracking);
return this.ToListAsync(queryable, cancellationToken);
}
/// <inheritdoc />
protected sealed override IAsyncEnumerable<TEntity> FindManyAsync(
EfTrackingOptions asNoTracking,
Expression<Func<TEntity, bool>> whereExpression,
CancellationToken cancellationToken = default)
{
var queryable = this.Queryable.Apply(asNoTracking)
.Apply(whereExpression);
return this.ToListAsync(queryable, cancellationToken);
} |
I can create a PR on Fluxera.Repository - but i'd like to discuss a few points:
IAsyncEnumerable<TEntity> FindManyAsync(
EfTrackingOptions asNoTracking,
CancellationToken cancellationToken = default)
Side question: Does your team have a Discord server or anything? I'd love to become more involved in your codebase and libraries if possible. If you have a Discord server or account, feel free to add me so that we can chat and collaborate more Thanks, My Dicscord Username: crypto_wizard#0725 |
Perhaps you can reference a value converter and 'manually' attach it each time? I'm not sure - im just thinking outloud. I will look into it as well, I don't use StronglyTypedId's yet with my code, but I plan to integrate them asap. I'm sure you have read this, but maybe the links will be helpful: Entity Framework Core
When I get around to implementing this, i'll test it and let you know my findings |
Hi! sorry for my late reply. I've been very busy the last few days.
Yes, absolutely. In fact, I'd like to have a single point of abstraction leakage to allow anything storage specific to be configured.
One big advantage of the Adding storage-specific APIs lite
in this example of yours you pass in a
I think it is the default at the moment. But maybe it would be a good idea to make
The team is I at the moment. :-D I don't have a discord server. But I could invite you to a Teams channel if you like. Cheers, |
There is actually a value converter available for the strongly typed IDs. I really don't know anymore what the problem was with them in combination with Using strongly typed IDs has saved me alot of headaches. I like this pattern a lot.
Yes, I read this article. The implementation of the calue onverter is somewhat based on it. I will have a look at the EF Core isses you linked when I have the time. Maybe I'll remember what the problem was and write a failing unit test for it. Thanks for your input. 👍 |
Another isse is that not every potential new underlying storage will support LINQ/ |
If we use AsNoTracking as default for EFCore queries we throw some exceptions because the attaching does not work with StronglyTypedIds somehow. Need to look into it.
The text was updated successfully, but these errors were encountered: