When are queries executed on DbContext - c#

I am trying to understand the performance impact of having one DbContext class vs multiple when using EF6 framework.
For example, if we have a simple DbContext such as this:
public class MainDbContext : DbContext
{
public DbSet<Car> Cars { get; set; }
public void AddCar(Car car)
{
Cars.Add(car);
SaveChanges();
}
}
Now let us say I have a service that uses the said DbContext in the following way:
public class CarService
{
public List<Car> Cars { get; private set; }
public CarService()
{
var dbContext = new MainDbContext();
Cars = dbContext.Cars.ToList();
}
}
At what point in time did the DbContext go to the database and retrieved all cars that are stored in the database? Was it when I called var dbContext = new MainDbContext(); or was it when I called Cars = dbContext.Cars.ToList();?
If former, if I had a DbContext that contains 100 tables, will all 100 tables be queried when the DbContext is created?

No. The query happens once you enumerate a table. Even in your second example, it still does not connect to the database.
It will connect when you enumerate, for example:
dbContext.Cars.ToList();
Or
foreach (Car c in dbContext.Cars)
Or bind the table to a UI control.
However when you make a where, order by, then by, join etc.. e.g.
var result = dbContext.Cars.Where(c => c.Id == 35).OrderBy(c => c.Name);
You will not connect to the database. You are only preparing the logical sequence of operations to translate into an SQL query.
AFTER THE QUESTION UPDATE
Now, your second example, with the ToList(), enumerate the results and connects to the database to get the data.

Related

Priority in retrieving Data from mysql database and filtering the retrieved data

I want to retrieve only one column of my data base. The following code works truly:
This is my customer in mysql-table/model-in-EF6
public partial class customers
{
public customers()
public int CustomerID { get; set; }
public string FullName { get; set; }
public string Mobile { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public string Image { get; set; }
}
public List<customers> GetAllCustomers()
{
return myContext.customers.ToList();
}
This is my question:
var GetOneColumn = myContext.CustomerRepository.GetAllCustomers().Select(f=>f.FullName);
Does it retrieve all columns from customers in database and then select only one column (FullName) from retrieved data or not, it retrieve just only one column(FullName) from data base? If it retrieve all data from data base what is the correct code (Linq)?
How could I find that??
Since you're using a .ToList() EF will
Retrieve all customers from database
Map them to customer objects
Later, when you compute GetOneColumn, you do a projection on them (iterating through already materialized object list)
To retrieve only one column,
Remove the .ToList() from the repository, and return a IQueryable<Customers>
Call a .ToList() after your select var GetOneColumn = myContext.CustomerRepository.GetAllCustomers().Select(f=>f.FullName).ToList();
So, your code would be
public IQueryable<customers> GetAllCustomers()
{
return myContext.customers;
}
// later in code
var GetOneColumn = myContext.CustomerRepository.GetAllCustomers().Select(f=>f.FullName).ToList();
See what's going on for yourself! Break up your code into steps and debug:
var allCustomers = myContext.CustomerRepository.GetAllCustomers();
var allCustomerNames = allCustomers.Select(f=>f.FullName);
Alternatively, run a profiler on your DB, or enable logging of queries in EF
To see all the queries that EF is generating you can do something like this
using (var context = new BlogContext())
{
context.Database.Log = Console.Write;
// Your code here...
}
See more details in the docs and Log Queries executed by Entity Framework DbContext
If you read this far, then it's worth knowing what will actually cause EF to send a query - see How Queries Work
Basically, this happens whenever you start enumerating through the elements of an IQueryable<T> (including LINQ methods like First(), Last(), Single(), ToList(), ToArray(), and the like)

Entity Framework: object returns with an empty list at first, but then suddenly the list is populated correctly

I have the following class:
public class User
{
public int Id { get; set; }
public List<User> Connections { get; set; }
//other properties
public User()
{
Connections = new List<User>();
}
}
Then I have a DataContext class for storage:
public class DataContext : DbContext
{
public DataContext() { }
public DataContext(DbContextOptions<DataContext> options) : base(options) { }
public virtual DbSet<User> Users { get; set; }
}
And a UserService class:
public class UserService: IUserService
{
private DataContext _context;
public UserService(DataContext context)
{
_context = context;
}
public User GetById(int id)
{
return _context.Users.Find(id);
}
...
}
Now suppose I correctly stored 2 users, and I add each other to their respective connection lists.
The problem is in the following piece of code:
var user1 = _userService.GetById(userId);
---> Here user1.Connections is an empty list (unexpected)
var results = anotherList.Select(x=>
{
---> Here user1.Connections have one object inside (the other user as expected)
});
I thought it was because the List was not populated yet since it was never accessed yet, but I also have a problem with the following endpoint in a controller:
var userId = int.Parse(User.Identity.Name);
var user1 = _userService.GetById(userId);
var connectionsInfo = user1.Connections.Select(x => new
{
Id = x.Id,
//map other properties
});
return Ok(connectionsInfo);
//this time an empty list is returned in the response, instead of a list with a single object
I read it might be regarding circular dependency, but I don't get any exception.
Also, I do not understand why in one case the list is populated after and in the other case is not populated at all.
Any idea what could be causing this?
Also I do not understand why in one case the list is populated after and in the other case is not populated at all.
It's the Lazy Loading feature in the entity framework. Lazy loading means delaying the loading of related data until you specifically request for it. For more explanation and a deep dive, you can review this good article.
Entity Framework supports three ways to load related data - eager loading, lazy loading, and explicit loading. for your scenario, It would prefer to use eager loading way. for achieving this goal EF has the Include() method. so, you can update your GetById method as below:
public User GetById(int id)
{
return _context.Users
.Include(item => item.Connections)
.Find(id);
}
With the above query when you find a specific user, its connections loads at the same time too. good luck.

Using multiple DbContext getting error Cannot use multiple DbContext instances within a single query execution

I started a new MVC app using .NET Core 3. I have three DbContext files that use three different databases: ComplaintDbContext for my main application, IdentityCoreDbContext for Identity users, and EmployeeDbContext for my employee database.
In my app I have a repository class called ComplaintRepository and the constructor looks like this:
public ComplaintRepository(ComplaintDbContext context,
EmployeeDbContext employeeContext)
{
_context = context;
_employeeContext = employeeContext;
}
In my ComplaintController I need to get data from both databases. I can get my data from my Complaint database, but once I call my Action that gets data from my Employee database I get the error:
Cannot use multiple DbContext instances within a single query execution. Ensure the query uses a single context instance.
I tried something like this:
public class FrameworkContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
}
public class ExtendedContext : FrameworkContext
{
public DbSet<Order> Orders { get; set; }
}
I cannot get it working. Any help would be appreciated. Thanks!
Edit:
I started a new repository called EmployeeRepository to separate concerns. Here is my Action that is giving me problems:
public IEnumerable<ApplicationUser> GetWorkerList()
{
var employees = _employeeRepository.GetEmployeesByUnit(22);
//Get ApplicationUsers where user exists in Employees list
IEnumerable<ApplicationUser> userList = _userManager.Users
.Where(emp => employees.Any(e => emp.EmployeeID == e.EmployeeId)).OrderBy(e => e.LastName);
return userList;
}
My Employee database and my Identity database both share a column called EmployeeId.
When I tried changing it to use ToList() I started getting a different error:
InvalidOperationException: The LINQ expression 'Where( source: DbSet, predicate: (a) => Any( source: (Unhandled parameter: __employees_0), predicate: (e) => a.EmployeeID == e.EmployeeId))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
Edit:
I used Tao Zhou's recommendation of using ToList() and I was able to get it working. I replaced IEnumerable in my repository to use this:
public List<TblEmployee> GetEmployeesByUnit(int unitId)
{
var emp = _context.TblEmployee.Where(e => e.UnitId == unitId &&
e.TermDate == null)
.OrderBy(e => e.LastName).ToList();
return emp;
}
In my controller I basically did the same and I now have this:
public List<ApplicationUser> GetWorkerList()
{
var employees = _employeeRepository.GetEmployeesByUnit(22);
List<ApplicationUser> userList = new List<ApplicationUser>();
//Get ApplicationUsers where user exists in Employees list
foreach (TblEmployee emp in employees)
{
ApplicationUser user = _userManager.Users
.Where(e => e.EmployeeID == emp.EmployeeId).FirstOrDefault();
userList.Add(user);
}
return userList;
}
I would like to use LINQ instead of the foreach loop.
I have also faced the same problem, from what I could tell, you cannot use different DbContext for same query. Just make sure you used same DbContext object for same query.
Since you are only using the EmployeeId in the Any method comparison, you could use Contains
var employees = _employeeRepository.GetEmployeesByUnit(22);
var employeeIds = employees.Select(x => x.EmployeeId).ToHashSet();
//Get ApplicationUsers where user exists in Employees list
IEnumerable<ApplicationUser> userList = _userManager.Users
.Where(emp => employeeIds.Contains(emp.EmployeeID)).OrderBy(e => e.LastName);
return userList;
// or return userList.ToList();

Does Entity size matter for performance in EF Core with DB first?

I'm writing a ASP.NET Core Web API project. As a data source It will be using existing (and pretty big) database. But not entire database. The API will use only some of the tables and even in these tables it will not use all the columns.
Using Reverse engineering and scaffolding I was able to generate DbContext and Entity classes... and it got me thinking. There is a table with 30 columns (or more). I'm using this table, but I only need 5 columns.
My question is:
Is there any advantage of removing 25 unused columns from C# entity object? Does it really matter?
The advantage of leaving them there unused is that in case of someone wants to add new functionality that will need one of them, he will not need to go to the db and reverse engineer needed columns (there are there already).
The advantage of removing unused is... ?
EDIT: Here is the sample code:
public class FooContext : DbContext
{
public FooContext(DbContextOptions<FooContext> options)
: base(options)
{
}
public DbSet<Item> Items { get; set; }
}
[Table("item")]
public class Item
{
[Key]
[Column("itemID", TypeName = "int")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("name", TypeName = "varchar(255)")]
public string Name { get; set; }
}
Sample usage:
public ItemDto GetItem(int id)
{
var item = _fooContext.Items.Where(i => i.Id == id).FirstOrDefault();
// Here I have item with two fields: Id and Name.
var itemDto = _mapper.Map<ItemDto>(item);
return itemDto;
}
Obviously I'm curious about more complex operations. Like... when item entity is being included by other entity. For example:
_foo.Warehouse.Include(i => i.Items)
or other more complex functions on Item entity
Your entity needs to match what's in the database, i.e. you need a property to match each column (neglecting any shadow properties). There's no choice here, as EF will complain otherwise.
However, when you actually query, you can select only the columns you actually need via something like:
var foos = await _context.Foos
.Select(x => new
{
Bar = x.Bar,
Baz = z.Baz
})
.ToListAsync();
Alternatively, if you don't need to be able to insert/update the table, you can instead opt to use DbQuery<T> instead of DbSet<T>. With DbQuery<T>, you can use anything class you want, and project the values however you like, via FromSql.

Global WHERE clause for items that are marked disabled

Recently I had to add a new column for a shop I've made, determining whether item should be available for sale or not.
Now the thing. Is it possible to do something like global where clause or I must to add it separately for each query, that relates to certain column (e.g. Products)? It would be really hard to correct every query and not miss anything.
Example query I use looks like this, but it's just a very basic one. Normally these where clasues are multiline, including selects from another tables.
DataBaseContext db = new DataBaseContext();
// ...
protected bool SomeFunction() {
// ...
var Products = db.Products.
Where(k => k.Active == true).
OrderByDescending(k => k.Date);
// ...
}
Normally I would do
var Products = db.Products.Where(k => k.Active == true);
Products = Products.
Where( ... ).
Select( ... ).
OrderBy( ... ).
...
Take( ... );
But there are mulptiple functions (db is common for every function in class), and I was thinking about writing the condition on the SQL server side, but I have no knowledge about that, sadly.
A simple solution would be to change your products implementation:
Old:
class DataBaseContext
{
//...
public DbSet<Product> Products { get; set; }
}
New:
class DataBaseContext
{
//...
public IQueryable<Product> Products
{
get
{
return this.Set<Product>().Where(pr => pr.IsActive == true);
}
}
}
However, this is not very robust and maintenance friendly, since you would have to do this for every type of item that can be activated. Also, you would need to create a second property of type DbSet called AllProducts and then vary between if you want to get active or allitems by checking all the points where the query is used.
Alternatively, you could create a wrapper for your DbContext:
interface IMyContext {
void SaveChanges();
IQueryable<T> Set<T>() where T: class
IQUeryable<T> GetActiveItems<T>() where T : SomeBaseClassWithActiveProperty
}
public class MyContext : IMyContext {
DataBaseContext _underylingContext = new DataBaseContext();
//... save changes implementation etc
public IQueryable<T> Set<T>()
where T : class
{
return _underlyingContext.Set<T>();
}
public IQueryable<T> GetActiveItems<T>()
where T : SomeBaseClassWithActiveProperty
{
return this.Set<T>().Where(item => item.IsActive == true);
}
}
then, when using it:
MyContext context = new MyContext();
var activeProducts = from p in context.GetActiveItems<Product>()
order p by p.Date //... or whatever;
var allProducts = from p in context.Set<Product>() //....
Either way, you should go by and check all calls to the Product DbSet and validate if you only need active items or all items.
You can do this inside the database with the following two steps:
(1) Rename your existing table to something else.
(2) Create a view with the name of the existing table:
create view <tablename> as
select *
from <newtablename>
where <your condition is true>;
(You probably want to list all the column instead of using *.)
Now all queries will use the view instead of the base table.
By the way, when designing an API for a database, it is a good idea to have all access be through views. This allows changes like this after the API is in place.

Categories

Resources