ASP.NET Entity FrameWork remove() + ToList() - c#

There is a list of managers (in fact a history), where current is marked as property with TillDate == null and IsIsCurrentManager = true.
db.MyManagers.Remove(db.MyManagers
.Where(e => e.ProjectId == projectId
&& e.MyManagerId == Id).First());
var newCurrentManager = db.MyManagers
.Where(e => e.ProjectId == projectId)
.OrderByDescending(i => i.FromDate)
.FirstOrDefault();
newCurrentManager.TillDate = null;
newCurrentManager.IsCurrentManager = true;
db.SaveChanges();
The problem is that Remove() method will only mark entity as deleted, but, as far as i can see, it still will be added to the list. How can deleted 1 record and than build a list of managers without it, without using SaveChanges() 2 times or hardcoding MyManagerList[2]

It's important to remember that Entity Framework is just an interface into your data store and all you are usually doing is building queries, queries that don't actually run until you materialise the results of a select (i.e. by enumerating through the results or calling something like ToList() or Single()) or, in the case of an update/delete operation, when you call SaveChanges().
So while you can do something like checking that the entity is tracked in the context as a deleted item, you are probably much better off sending the results to the database by calling SaveChanges() twice.
The secondary benefit of that is that another query that doesn't use the same context object (e.g. another web request) will be able to see that the item has been deleted while the original query is trying to pull the list of managers.

You can query the Entity State to exclude it from the second list.
I also have updated the linq request with some cleaning.
Also there is no chek for null, your db query can return null and should be managed as such.
db.MyManagers.Remove(db.MyManagers
.FirstOrDefault(e => e.ProjectId == projectId
&& e.MyManagerId == Id));
var newCurrentManager = db.MyManagers
.ToList()
.Where(e => e.ProjectId == projectId
&& db.Entry(e).State != EntityState.Deleted)//This will query the entity traker
.OrderByDescending(i => i.FromDate)
.FirstOrDefault();
newCurrentManager.TillDate = null;
newCurrentManager.IsCurrentManager = true;
db.SaveChanges();

Related

LINQ not evaluating check for null object

I'm wondering why my linq statement doesn't properly evaluate the null check on the Agency object which is a property of the Users model.
var invalidUsers = this.DbContext.Users.Where(p => p.Agency != null).ToList();
var invalidUsersList = invalidUsers.Where(p => p.Agency != null).ToList();
When I run the code above, the first line returns a list of all Users, regardless of the Agency object being null or not. However, the second line executes and properly filters the list and returns a list that is correctly returning Users where Agency is not null.
Chances are since this appears to be a foreign key table you need to include it first in LINQ so it can query against it.
So something like.
var invalidUsers = await this.DbContext.Users
.Include(p => p.Agency)
.Where(p => p.Agency != null)
.ToListAsync();
Give that a try and see if it helps.

LINQ include a subset of a query

I have an Entity in EF Core that is using the structure like:
Course has an entity CourseUserRoles which has the CourseId, UserId and RoleId.
CourseViewModel has the same structure as Course, except CourseUserRoles, instead it has two booleans IsAdministrator and IsContributor, that are related to the RoleId.
I am trying to make a query that won't bring all CourseUserRoles for every course queried, but only the ones specific for that user.
I saw that syntactically the below is correct:
query = query.Include(x => x.CourseUserRoles.Where(y => y.UserId == userId));
where the query is trying to return a list of courses, I just want to include the ones that have the same Id as the user.
The problem is that the above is throwing an exception.
Is it possible to only include the CourseUserRoles when the course has the UserId? If it doesn't have it would return null or empty list.
I typically do this by creating separate queries and concatenating as follows:
query = query.Where(x => x.CourseUserRoles.UserId != userId)
var queryWithInclude = query.Where(x => x.CourseUserRoles.UserId == userId)
.Include(x => x.CourseUserRoles)
var fullDataset = query.Concat(queryWithInclude);
This keeps both query objects as type IQueryable and not IEnumerable, allowing execution to happen in SQL/server-side rather than in memory.

Entity Framework code-first IQueryable navigation property

Most of the examples I see on the internet show the navigation properties as either ICollection or straight List implementation. They are usually virtual, to enable lazy-loading.
However, when you access such property, it will load the entire collection in memory and if you have a subquery after it (i.e. object.MyListProperty.Where(...)) I have noticed that an SQL query will be issued for each item in the MyListProperty.
How do I avoid this? I want the where clause after the list property to execute on the SQL server, if possible. Can I use an IQueryable navigation property? Is there any best-practice for such case?
My advice for best practise is to disable Lazy loading altogether. Instead force the caller to eagerly load navigation properties through include statements or by using projections.
There are 3rd party products that support include with filters, as described in this post: How to filter include entities in entity framework, but in my experience this further complicates down-stream processing of the objects that are retrieved. If the entity object is loaded outside of method X, because method X can't know for sure if the navigation properties have been loaded with the correct filters, method X starts off by re-querying for the precise rows that it knows it needs.
using (var context = new MyDbContext())
{
context.Configuration.LazyLoadingEnabled = false;
// TODO: load your data
...
}
In this way the records will only be loaded when they are explicitly requested.
When you want access to an IQueryable so you can defer the loading of the data, then make those queries against the DbContext instance and not from the object.
In this example assume that a Customer has many thousands of transactions, so we don't want them to be eagerly or lazy loaded at all.
using (var context = new MyDbContext())
{
context.Configuration.LazyLoadingEnabled = false;
var customer = context.Customers.First(x => x.Id == 123);
...
// count the transactions in the last 30 days for this customer
int customerId = customer.Id;
DateTime dateFrom = DateTime.Today.AddDays(-30)
// different variations on the same query
int transactionCount1 = context.Customers.Where(x => x.Id == customerId)
.SelectMany(x => x.Transactions.Where(x => x.TransactionDate >= dateFrom))
.Count();
int transactionCount2 = context.Customers.Where(x => x.Id == customerId)
.SelectMany(x => x.Transactions)
.Where(x => x.TransactionDate >= dateFrom)
.Count();
int transactionCount3 = context.Transactions.Where(x => x.CustomerId == customerId)
.Where(x => x.TransactionDate >= dateFrom)
.Count();
}
It is good that you have identified that you want to use an IQueryable<T> we access them from the DbContext directly, not from the instances that were previously retrieved.

Return Selected Properties of Single Object in EF Method Syntax

//This works, but seems incorrect to me
Object selection = db.ExampleTable
.Where(s => s.Id == id)
.Select(s => new { s.Id, s.PropIWantToShow })
.SingleOrDefault();
//This seems correct, but does not work
Object selection = db.ExampleTable
.SingleOrDefault(s => s.Id == id)
.Select(s => new { s.Id, s.PropIWantToShow });
db is our Entity Framework data context.
My goal is to select a single entry matching the provided id in ExampleTable. If an entry is not found, this is to return null. However, EF doesn't seem to let me select a single object and then only return specific properties. How do I accomplish this or is the first example I provided correct?
I did check this question:
select properties of entity ef linq:
Unfortunately you cannot conditionally load properties of related entity - you either load whole door entity, or don't include that entity.
But the answer just doesn't seem right, but obviously "seems" is a very weak statement.
Your first method is correct:
//This works, but seems incorrect to me
Object selection = db.ExampleTable
.Where(s => s.Id == id)
.Select(s => new { s.Id, s.PropIWantToShow })
.SingleOrDefault();
Your second method gets you a single object, not an IQueryable<T> object that LINQ would work with. If you want to convert from one type of object to another, that isn't a LINQ thing. You can still, but it'll be more convoluted. Something like:
var selection =...;
var newselection=new { Id=selection.Id, PropIWantToShow=selection.PropIWantToShow };
but this is very bad because you DID retrieve the entire object from the DB, and then just threw away most of it. Your first method only returns 2 fields from the DB.
If you want your function to return null if condition doesn't match then use FirstorDefault() instead of SingleorDefalut(). So if you want to match an id and return an object then do it like this :
return db.ExampleTable.FirstorDefault(c=>c.Id == id);

Entity framework: How to reduce database hits?

So I have this query in my repository (also using Unit of Work pattern) which uses eager loading to make one hit to the database:
from g in _context.Games.Include(pg => pg.PreviousGame).Include(go => go.GameObjects)
where EntityFunctions.DiffMilliseconds(DateTime.Now, g.EndDate) > 0
&& g.GameTypeId == (int)GameTypes.Lottery
&& g.GameStatusId == (int)GameStatues.Open
select new LotteryModel
{
EndDate = g.EndDate,
GameId = g.Id,
PreviousGameEndDate = g.PreviousGame.EndDate,
PreviousGameId = g.PreviousGameId.HasValue ? g.PreviousGameId.Value : 0,
PreviousGameStartDate = g.PreviousGame.StartDate,
PreviousWinningObjectCount = g.PreviousGame.GameObjects.Select(go => go.Object.Count).FirstOrDefault(),
PreviousWinningObjectExternalVideoId = g.PreviousGame.GameObjects.Select(go => go.Object.Video.ExternalVideoId).FirstOrDefault(),
PreviousWinningObjectName = g.PreviousGame.GameObjects.Select(go => go.Object.Video.Name).FirstOrDefault(),
StartDate = g.StartDate,
WinningObjectCount = g.GameObjects.Select(go => go.Object.Count).FirstOrDefault(),
WinningObjectExternalVideoId = g.GameObjects.Select(go => go.Object.Video.ExternalVideoId).FirstOrDefault(),
WinningObjectName = g.GameObjects.Select(go => go.Object.Video.Name).FirstOrDefault()
};
However I'm reluctant to use this because I now have to create a separate LotteryModel object to return up throughout my other layers.
I would like to be able to return an entity of type "Game" which has all of the navigational methods to all of my other data (PreviousGame, GameObjects, etc) and then map the needed properties to my flat view model, but when I do this it seems to only lazy load the objects and then I have the additional hits to the DB.
Or do I have this wrong and whenever I need to return heirarchical data I should return it through my LINQ query in the select portion?
My basic goal is to reduce the hits to the DB.
I don't really understand the problem. You return your Games object and you can access the properties and subobjects off it. Your use of the Include() method tells it to load what you need, and not lazy load it.
Make sure you return a single object via a .First, .FirstOrDefault, .Single, .SingleOrDefault, or similar methods.
I ended up with this query (FYI I'm using the System.Data.Objects namespace for the Include extension):
(from g in _context.Games.Include(pg => pg.PreviousGame.GameObjects.Select(o => o.Object.Video))
.Include(go => go.GameObjects.Select(o => o.Object.Video))
where EntityFunctions.DiffMilliseconds(DateTime.Now, g.EndDate) > 0
&& g.GameTypeId == (int)GameTypes.Lottery
&& g.GameStatusId == (int)GameStatues.Open
select g).FirstOrDefault();
I guess I just needed to include more of the heirarchy and didn't know I could use Select() in the Include() function!

Categories

Resources