When is the right time to make ToList on an entity framework query - entity-framework

This is called in my service. Now I have asked myself when should I do the .ToList() on the query to materialize the entities?
Do I have to do it at all? Because my application layer will anyway convert the entities to a json array and finally then the enumerator must be enumerated...
Should I follow this thinking? Or is it better to only return a materialized collection to my application layer?
IEnumerable<BrowseSchoolyearDTO> ISchoolyearService.GetAll(int userId)
{
return _context.Schoolyears
.Where(s => s.UserId == userId)
.Select(s => s.ToBrowseSchoolyearDto())
.ToList();
}

You don't need to do a .ToList at all, as long as all your operations on the DTO is done while the db connection is active. You only need to do a .ToList if you need the list to be enumerated and returned outside of the db transaction.
So if your code looks something like this:
Edit: changed repository to service as you don't use repositories
using(var rep = new service())
{
var list = rep.GetAll(1);
return list.Select(x => new DTOViewModel(x)).ToList();
}
you don't need .ToList
If your code looks something like this:
using(var rep = new service())
{
var list = rep.GetAll(1);
}
return list.Select(x => new DTOViewModel(x));
Then you do need a .ToList
Edit to show more of method
public void DoSomeOperationCalledFromWeb(int id)
{
IEnumerable<DTO> list;
using(var serv = new Service())
{
list = rep.GetAll(1).ToList();
/** OR if you need additional filtering **/
list = rep.GetAll(1).Where(/**some filtering**/).ToList();
}
/** Do operations on list **/
}

Related

Linq to get a list of models per Make into DTO

I have a DTO class like this
public string Make { get; set; };
public List<string> Models { get; set; }
Then there is a table which contains a list of vehicles, with make and model columns.
My API endpoint accepts a list of strings (the Makes)
I need to return a list of the DTO class with each make and the list of models.
public async Task<ActionResult<List<MakeModelDTO>>> GetModelsByMakes([FromQuery] List<string> make_list)
{
return await _context.Vehicles.Where(x => x.Make.????).Select(x => x.Model).Distinct().ToListAsync();
}
I don't even want to show the code I've tried, because all versions turned out to be a mess.
I know this is suppose to be a very simple task, I just can't figure it out.
Firstly, In condition you can do this
Where(x => make_list.Contains(x.Make)
or
Where(x => make_list.Any(m => m == x.Make)
Secondly, the method is returning List<MakeModelDTO>, So you should adjust select result like below
.GroupBy(p => p.Make).Select(g =>
new MakeModelDTO { Make = g.Key, Models = g.Select(p => p.Model).ToList() }).ToListAsync();
FullCode
public async Task<ActionResult<List<MakeModelDTO>>> GetModelsByMakes([FromQuery] List<string> make_list)
{
return await _context.Vehicles.Where(x => make_list.Contains(x.Make)).GroupBy(p => p.Make).Select(g =>
new MakeModelDTO { Make = g.Key, Models = g.Select(p => p.Model).ToList() }).ToListAsync();
}
As #Fabio suggested you can filter the make like that. Adding to it convert to your DTO you need to group by that also.
You could try something like this
public async Task<ActionResult<List<MakeModelDTO>>> GetModelsByMakes([FromQuery] List<string> make_list)
{
var resultAsDto = await _context.Vehicles
.Where(vehicle => makeList.Contains(vehicle.Make))
.GroupBy(v=>v.Make)
.Select(g=> new YourDto
{
Make= g.Key -- As it grouped by make
Models = g.Select(v=>v.Model)
}).ToListAsync()
return resultAsDto
}

How do I reuse an object instance in an Observable chain?

In rx, how do you handle the need to reuse an object instance in one step in the next step? For example, I need to get a context in the ORM to then act upon. Async/Await is in the syntax below:
public async Task<IList<string>> Delete(IList<string> ids)
{
var context = await _contextFactory.CreateContext();
context.Set<T>().RemoveRange(
context.Set<T>().Where(item => ids.Contains(item.Id)));
return ids;
}
An Observable version is
public IObservable<string> DeleteObservable(IList<string> ids)
{
return ids.ToObservable()
.Select(i =>
{
var context = await _contextFactory.CreateContext();
context.Set<T>().RemoveRange(
context.Set<T>().Where(item => item.Id == id));
return id;
});
}
However, I don't want to create a new context every time I delete an item. I want to create a context and then reuse it in the select. How should I do that?
Yes, in this example it would be best to also buffer and submit the ids together, but this was just an example for my question. I hope that part is not distracting.
The more idiomatic way of doing it is like this:
public IObservable<string> DeleteObservable(IList<string> ids)
{
return Observable.Using(
async () => await _contextFactory.CreateContext(),
context =>
ids.ToObservable().Select(i =>
{
context.Set<T>().RemoveRange(context.Set<T>().Where(item => item.Id == i));
return i;
}));
}
The Observable.Using method creates a disposable resource that gets disposed when the subscription to the observable closes.
The only problem with this is that the statement context.Set<T>().RemoveRange(context.Set<T>().Where(item => item.Id == i)); just shouldn't be inside an observable like that. Rx is about queries. Any changes should be made in a .Subscribe method.
What are you trying to achieve?
I think I got it and the answer keeps ended up being 'SelectMany'. I guess I'm still getting used to these operators.
public IObservable<string> DeleteObservable(IList<string> ids)
{
return Observable
.Return(_contextFactory)
.SelectMany(factory => factory.CreateContext())
.Zip(ids.ToObservable(), (dbContext, entityId) =>
{
dbContext.Set<T>().RemoveRange(
dbContext.Set<T>().Where(item => item.Id == entityId));
return entityId;
});
}

Entity Framework 6 Generic Eager Loading Query Method

I am writing a generic querying method in Entity Framework 6, based off of this helpful article. Here's how it looks:
public static T QueryEagerLoad<T>(Expression<Func<T, bool>> match) where T : class
{
using (var databaseContext = new ClearspanDatabaseContext())
{
databaseContext.Configuration.LazyLoadingEnabled = false;
T retrievedObject = databaseContext.Set<T>().SingleOrDefault(match);
return retrievedObject;
}
}
I'm attempting to eagerly load any related entities, so I include disable to configuration variable LazyLoadingEnabled. While it loads the object, it does not load the related entities, per my view in the debugger. Why would this be? Am I missing something? I should note that I'm using Npgsql. Thanks in advance.
See Mikael Östberg's answer to this question. To use a generic method for querying with eager loading, it seems necessary to inject the includes. Here's how the generic method shaped up:
public static T Query<T>(Expression<Func<T, bool>> match, List<Expression<Func<T, object>>> includes) where T : class
{
using (var databaseContext = new ClearspanDatabaseContext())
{
var dataSet = databaseContext.Set<T>(); // Get the relevant DataSet
T retrievedObject = includes.Aggregate( // Eagerly load the passed navigation properties
dataSet.AsQueryable(),
(current, include) => current.Include(include)
).SingleOrDefault(match); // Find exactly one or zero matches
return retrievedObject;
}
}
And an example of a call that injects the properties to eagerly load (the includes parameter in the generic method above):
public static Lumber GetLumber(int databaseId)
{
Expression<Func<Lumber, object>> lengthProperty = (lumber => lumber.Length);
Expression<Func<Lumber, object>> thicknessProperty = (lumber => lumber.Thickness);
Expression<Func<Lumber, object>> widthProperty = (lumber => lumber.Width);
List<Expression<Func<Lumber, object>>> lumberNaviationProperties = new List<Expression<Func<Lumber, object>>>() { lengthProperty, thicknessProperty, widthProperty };
Lumber retrievedLumber = DatabaseOperations.Query<Lumber>((lumber => lumber.DatabaseId == databaseId), lumberNaviationProperties);
return retrievedLumber;
}

Clear related items followed by parent entity update - Many to Many EntityFramework with AutoMapper

I am trying to remove all references followed by adding them back from a list of disconnected objects.
using(var scope = new TransactionScope())
{
_autoIncidentService.AddNewCompanyVehicles(
autoIncidentModel.CompanyVehiclesInvolved.Where(v => v.Id == 0));
_autoIncidentService.ClearCollections(autoIncidentModel.Id);
_autoIncidentService.Update(autoIncidentModel);
scope.Complete();
}
return Json(ResponseView);
The ClearCollections removes items references. The GetAutoIncident includes the collection.
public void ClearCollections(int id)
{
var autoIncident = GetAutoIncident(id);
foreach (var vehicle in autoIncident.CompanyVehiclesInvolved.ToArray())
{
autoIncident.CompanyVehiclesInvolved.Remove(vehicle);
}
db.SaveChanges();
}
When I try to update the entity right after the ClearCollections method it fails.
The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
I am using a singleton to get the DbContext so there shouldn't be any situation where the context is different. It is being stored in the HttpContext.Current.Items.
The update method is as follows:
public override void Update(AutoIncidentModel model)
{
var data = GetData(model.Id);
Mapper.CreateMap<AutoIncidentModel, AutoIncident>()
.ForMember(m => m.CompanyVehiclesInvolved, opt => opt.ResolveUsing(m =>
{
var ids = m.CompanyVehiclesInvolved.Select(v => v.Id);
return db.Vehicles.Where(v => ids.Contains(v.Id)).ToList();
}));
Mapper.Map(model, data);
db.SaveChanges();
}
Obviously, I am missing something important here. Do the entites from my ResolveUsing method need to somehow be associated with the parent entity or is automapper overwriting the property (CompanyVehiclesInvolved) and causing a problem?

How can I update my DTO's ID when inserting multiple new entities

I'm using EF4. I'm adding a series of new entities from a list of DTOs, and I'm not saving changes until after all of them are added. I'm wanting to set the IDs of the DTOs to what the new entities' IDs are. How on earth do I do this? Does EF provide a mechanism for this?
With a single entity I would do this:
public void InsertMyDto(MyDto a_dto)
{
var newEntity = new MyEntity
{
Name = a_dto.Name,
Type = a_dto.Type.ToString(),
Price = a_dto.Price
};
_dataContext.MyEntities.AddObject(newEntity);
_dataContext.SaveChanges();
a_dto.ID = newEntity.ID;
}
This works fine, but what do I do in this case?
public void InsertMyDtos(IEnumerable<MyDto> a_dtos)
{
foreach (var myDto in a_dtos)
{
var newEntity = new MyEntity
{
Name = myDto.Name,
Type = myDto.Type.ToString(),
Price = myDto.Price
};
// Does some validation logic against the database that might fail.
_dataContext.MyEntities.AddObject(newEntity);
}
_dataContext.SaveChanges();
// ???
}
I want to save all at once, because I have validation work (not shown above) that is done against the database and fails before it gets to SaveChanges, and if it fails I want it to fail as a whole transaction (i.e. rollback).
I don't think that EF can help you here. It even can't help you for a single instance which forces you to write a_dto.ID = newEntity.ID. The counterpart of this code for multiple entites is to keep track of the pairs of dtos and new entities:
public void InsertMyDtos(IEnumerable<MyDto> a_dtos)
{
Dictionary<MyDto, MyEntity> dict = new Dictionary<MyDto, MyEntity>();
foreach (var myDto in a_dtos)
{
var newEntity = new MyEntity
{
Name = myDto.Name,
Type = myDto.Type.ToString(),
Price = myDto.Price
};
dict.Add(myDto, newEntity);
// Does some validation logic against the database that might fail.
_dataContext.MyEntities.AddObject(newEntity);
}
_dataContext.SaveChanges();
foreach (var item in dict)
item.Key.ID = item.Value.ID; // Key is MyDto, Value is MyEntity
}