EF Core IgnoreQueryFilters behavior on multiple subsequent queries - entity-framework

i've a problem with the IgnoreQueryFilters.
I've implemented soft-delete using the HasQueryFilter ( in the OnModelCreating i apply the global query filter to every entity which implements a particular interface ).
The problem is that if i launch a query 2 times in the same request:
the first time asking for also the "IsDeleted = true" entities ( so including IgnoreQueryFilters ),
and the second time asking only for the "IsDeleted = false" ( so not including the IgnoreQueryFilters)
the second time i still get also the "deleted" entities.
I think that this happens because when i launch the query for the second time, the entities are already in the context and i get them instead of the right results.
Here how i build the method for "including / excluding" the deleted entities.
// this is my repo pattern implementation
public class MyEntityRepo()
{
....
public Task<List<MyEntity>> GetEntityByUserId(int userId, bool ignoreQueryFilter = false)
{
var query = context.blabla
.Include(c => c.blabla2)
.Where(c => c.ApplicationUserId == userId);
if (ignoreQueryFilter)
{
query = query.IgnoreQueryFilters();
}
var result = await query.ToListAsync();
return result;
}
}
Now if in a service i call it this way:
public void MyServiceMethod()
{
...
var IncludeDeleted = await myEntityRepo.GetEntityByUserId(1, true);
//Here i need to do a sync with other data and for this reason i need also the deleted entities
foreach( var e in includeDeleted)
{
// do something
}
await context.SaveChangesAsync();
//Now that my data is correctly synced i've to get the data again but this time excluding the deleted entities
// and it fails
var ExcludeDeleted = await myEntityRepo.GetEntityByUserId(1, false);
return ExcludeDeleted;
}
The only way i found to solve the problem is to do something like context.ChangeTracker.Clear() before the second call to myEntityRepo.GetEntityByUserId, but is this the right way to go?
Since in real the method is a little bit more complex and can be re-used in other areas, i'm not sure that calling a Clear is a good idea because tomorrow it might be called in a bigger method and cause unexpected problems.
What's the best practice to use when i need to get data with and without query filter?
Is it ok to clear the change tracker?
If yes, what's the best time to clear it? in the GetEntityByUserId if i just ignoredTheFilters ( for consistency ) or after, in the caller method, whenever i find a problem like this one?
Actually i've also thinked about removing the GlobalQueryFilter usage and replace it with methods in the repos that get or exclude deleted entities... yes i've to remember to always filter out but feels more practical.

Related

Entity Framework DbContext Update Fails if No Change in Field Values

When we pass our DbContext an object whose values have not changed, and try to perform an Update we get a 500 internal server error.
A user may open a dialog box to edit a record, change a value, change it back and then send the record to the database. Also we provide a Backup and Restore function and when the records are restored, some of them will not have changed since the backup was performed.
I was under the impression that a PUT would delete and re-create the record so I didn't feel there would be a problem.
For example, having checked that the Activity exists my ActivityController is as follows:
var activityEntityFromRepo = _activityRepository.GetActivity(id);
// Map(source object (Dto), destination object (Entity))
_mapper.Map(activityForUpdateDto, activityEntityFromRepo);
_activityRepository.UpdateActivity(activityEntityFromRepo);
// Save the updated Activity entity, added to the DbContext, to the SQL database.
if (await _activityRepository.SaveChangesAsync())
{
var activityFromRepo = _activityRepository.GetActivity(id);
if (activityFromRepo == null)
{
return NotFound("Updated Activity could not be found");
}
var activity = _mapper.Map<ActivityDto>(activityFromRepo);
return Ok(activity);
}
else
{
// The save failed.
var message = $"Could not update Activity {id} in the database.";
_logger.LogWarning(message);
throw new Exception(message);
};
My ActivityRepository is as follows:
public void UpdateActivity(Activity activity)
{
_context.Activities.Update(activity);
}
If any of the fields have changed then we don't get the error. Do I have to check every record for equality before the PUT? It seems unnecessary.
Perhaps I have missed something obvious. Any suggestions very welcome.
There is a lot of code missing here.
In your code you call your SaveChangesAsync (not the EF SaveChangesAsync).
Probably (but there is not the code to be sure) your SaveChangesAsync is something that returns false if there is an exception (and is not a good pattern because you "loose" the exception info) or if DbSet.SaveChangesAsync returns 0.
I think (but there is a lot of missing code) that this is your case. If you don't make any changes, SaveChangesAsync returns 0.
EDIT
The System.Exception is raised by your code (last line). EF never throws System.Exception.

In Entity Framework Core, with one query, how do I select objects by the order of a parent object?

I'm working in Entity Framework Core 2.0, and I'm trying to do something like this. I want a list of all the classes taught by the oldest teacher. I tried to make the two requests into one query, but I couldn't make it work. Is there a concise way to make this one query?
private async Task<Teacher> GetOldestTeacher(int schoolId)
{
using (var db = new SchoolContext())
{
return await db.Teacher
.Where(t => t.SchoolId == schoolId)
.OrderByDescending(t => t.DateOfBirth)
.FirstAsync();
}
}
public async Task<IEnumerable<Class>> GetOldestTeachersClasses(int schoolId)
{
var oldestTeacher = await GetOldestTeacher(schoolId);
using (var db = new SchoolContext())
{
return await db.Class
.Where(c => c.TeacherId == oldestTeacher.Id && c.SchoolId == schoolId)
.ToListAsync();
}
}
This isn't exactly my code, but it's close enough to what I'm shooting for. This works, but I'm looking to make it more efficient. Any help would be appreciated.
Assuming there exists a navigation property called Classes in the Teacher class, you could do this:
public async Task<IEnumerable<Class>> GetOldestTeachersClasses(int schoolId)
{
using (var db = new SchoolContext())
{
return await db.Teacher
.Where(t => t.SchoolId == schoolId)
.OrderByDescending(t => t.DateOfBirth)
.Take(1)
.SelectMany(t => t.Classes)
.ToListAsync();
}
}
If you use First, you inevitably ask EF to immediately load the object into memory. Because of that, if you use that returned object to do the further parts of the query, you would actually be doing at least 2 roundtrips to the database and performing 2 queries instead of one despite in your C# code there being only one expression.
The trick here is that you filter the teachers just as you've already done, and instead of loading the first one using First(), you Take() the topmost one, according to the ordering requirements. The key difference is that Take will not actually load the object immediately - instead, it enables you to further specify the query which will be translated to one proper SQL query.
The reason for using SelectMany() is that semantically, there still can be more than one teachers as the Take() call actually returns an IQueryable<Teacher> object. But the semantics of your filter criteria ensures that in reality, there will only be 0 or 1 teacher, so collecting his/her classes using SelectMany will result in only his/her taught classes. You could also do the same by doing a Join as DevilSuichiro remarked in the comments.
However, make sure you verify the generated SQL. I personally don't yet have experience with EF Core 2.0 only with 1.x, which sometimes failed to translate rather trivial queries to proper SQL statements and instead performed a lot of work in my app. The last time I checked the 2.0 roadmap it was promised to significantly improve the translator, but I've written this code just from the top of my head so a proper verification is required.

Merging a changing collection of observables

We have a class Thing that implements IObservable<Thing>. In another class, there is a collection of Things , and that class needs to react to updates from all those observables in a unified manner. The obvious way to do that is Observable.Merge(), and that generally works; however, when the collection changes, we also need to subscribe to any new Things in our merged subscription (and in theory unsubscribe from all the removed ones, but that seems less problematic - they just won't produce any updates anymore).
We currently achieve that by recreating the subscription on every change of the collection, but that seems rather suboptimal in terms of processing overhead and also due to missing updates from any of the Things in the brief time between discarding the old subscription and creating the new one (which has proven to be an issue in practice, especially as we also need to Buffer() the subscription for a short amount of time, and the buffered items are lost when disposing the subscription).
What is the proper way of merging a changing collection of observables like this?
If you have an IObservable<IObservable<T>> observable, then calling Merge on that, will include children of new parents, if you catch my drift. The trick is converting the ObservableCollection<IObservable<Thing>> to an IObservable<IObservable<Thing>>.
If you have ReactiveUI running around, and are ok to use it, then you could convert the ObservableCollection<IObservable<Thing>> to a ReactiveCollection<IObservable<Thing>>. ReactiveCollection inherits from ObservableCollection, and also implements IObservable.
If ReactiveUI is out of the question (which I'm guessing it is because you're already using a Caliburn Micro collection), then you can convert using ObservableCollection's events:
ObservableCollection<IObservable<Thing>> observableCollection = new ObservableCollection<IObservable<Thing>>();
IObservable<IObservable<Thing>> oCollectionObservable = Observable.FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
h => observableCollection.CollectionChanged += h,
h => observableCollection.CollectionChanged -= h
)
.SelectMany(ep => ep.EventArgs.NewItems.Cast<IObservable<Thing>>());
Here's some sample code demonstrating use:
oCollectionObservable
.Merge()
.Subscribe(t => Console.WriteLine($"Received Thing {{Id = {t.Id}}}"));
var firstObservable = Observable.Range(1, 5)
.Select(i => new Thing { Id = i })
.Concat(
Observable.Range(8, 5)
.Select(i => new Thing { Id = i })
.Delay(TimeSpan.FromSeconds(2))
);
observableCollection.Add(firstObservable);
var subject = new Subject<Thing>();
observableCollection.Add(subject);
subject.OnNext(new Thing { Id = 6 });
subject.OnNext(new Thing { Id = 7 });
Using the following class:
public class Thing
{
public int Id { get; set; }
}

querying in EntityObject

I have partial class TaxReportItem with partial method OnActualVolumeChanging(double value):
public partial class TaxReportItem
{
partial void OnActualVolumeChanging(double value)
{
if (Tax != null)
{
Payment = value*Tax.TaxRate;
}
}
}
In this method i want to get all collection of TaxReportItems that's present in context(something like this):
partial void OnActualVolumeChanging(double value)
{
var sum = 0.0;
if (Tax != null)
{
Payment = value*Tax.TaxRate;
foreach (var taxReportItem in ????)
{
sum += taxReportItem.Sum;
}
}
}
How can i achieve this?
This is actually quite hard because you should never need this. If you need this, design of your entity object is wrong and it is doing something which should be done elsewhere. Single TaxReportItem should never need to know about other tax report items and load them from database unless other items are dependent on this item (they form an aggregate). In such case you should have navigation property to dependent items in the principal one.
To follow your question. If you need to load other items you must have instance of the context to do that. You can either get instance used to load current item and use it to load other entities (bad solution) or you can create a new instance of the context and use it to load other entities (even worse solution).
As you can see from the linked article getting current context instance from the entity itself is not easy and it has some prerequisites which goes back to the first paragraph. It is hard because it is wrong approach.

Entity Framework and Entity Tracker Problems

If I run the following code it throws the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
public void Save(Category category)
{
using(var db = new NorthwindContext())
{
if(category.CategoryID == 0)
{
db.AddToCategorySet(category);
}
else
{
//category.RemoveTracker();
db.Attach(category);
}
db.SaveChanges();
}
}
The reason is of course that the category is sent from interface which we got from GetById method which already attached the EntityChangeTracker to the category object. I also tried to set the entity tracker to null but it did not update the category object.
protected void Btn_Update_Category_Click(object sender, EventArgs e)
{
_categoryRepository = new CategoryRepository();
int categoryId = Int32.Parse(txtCategoryId.Text);
var category = _categoryRepository.GetById(categoryId);
category.CategoryName = txtUpdateCategoryName.Text;
_categoryRepository.Save(category);
}
I'm still learning Entity Framework myself, but maybe I can help a little. When working with the Entity Framework, you need to be aware of how you're handling different contexts. It looks like you're trying to localize your context as much as possible by saying:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
...
}
}
... within your data access method. Did you do the same thing in your GetById method? If so, did you remember to detach the object you got back so that it could be attached later in a different context?
public Category GetById(int categoryId)
{
using (var db = new NorthwindContext())
{
Category category = (from c in db.Category where Category.ID == categoryId select c).First();
db.Detach(category);
}
}
That way when you call Attach it isn't trying to step on an already-attached context. Does that help?
As you pointed out in your comment, this poses a problem when you're trying to modify an item and then tell your database layer to save it, because once an item is detached from its context, it no longer keeps track of the changes that were made to it. There are a few ways I can think of to get around this problem, none of them perfect.
If your architecture supports it, you could expand the scope of your context enough that your Save method could use the same context that your GetById method uses. This helps to avoid the whole attach/detach problem entirely, but it might push your data layer a little closer to your business logic than you would like.
You can load a new instance of the item out of the new context based on its ID, set all of its properties based on the category that is passed in, and then save it. This costs two database round-trips for what should really only need one, and it isn't very maintainable.
You can dig into the context itself to mark the Category's properties as changed.
For example:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
db.Attach(category);
var stateEntry = db.ObjectStateManager.GetObjectStateEntry(category);
foreach (var propertyName in stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(fm => fm.FieldType.Name)) {
stateEntry.SetModifiedProperty(propertyName);
}
db.SaveChanges();
}
}
This looks a little uglier, but should be more performant and maintainable overall. Plus, if you want, you could make it generic enough to throw into an extension method somewhere so you don't have to see or repeat the ugly code, but you still get the functionality out of it.