Does creating DbContext per query in Asp.net make EF only read the data from its cache, or does it query DB for the whole sets every time? I know about metadata caching per AppDomain, but what about just the data?
Context: data acquisition and visualisation application with MVC4 + Web API frontend, wouldn't call that "high volume", but lots of queries return the same sets of data in some shorter time frame.
Entity Framework doesn't have a data cache per AppDomain, only a cache per context instance.
If you create a new context per request or query you start with an empty cache and EF will fetch the data from the database.
Moreover, the term "cache per context instance" can be misleading as it doesn't mean that EF won't run queries to the database if the entities are already loaded in the context cache. The way how this cache works and how you can leverage it (or not) is the following:
Every LINQ-to-Entities query on a DbSet<T> or generally on an IQueryable<T> will run a database query, no matter if the entities already exist in the context or not. But if an entity with the same key as a queried entity already exists in the context EF will throw the result of that query away and return the cached entity instance back to the caller.
It does this check if the entity with the same key exists after it has run the query. (For complex queries - for example queries that contain an Include - it can't do this check before because it cannot know which entities and key values will be returned.)
That's the default behaviour (MergeOption is AppendOnly). You can change this behaviour to OverwriteChanges and other options, I believe, but none of them will avoid that LINQ queries always issue database queries.
For querying an entity just by its key you can use GetObjectByKey or Find (with DbContext) which will check first if the entity with that key is already cached in the context and then return this cached object. If not it will run a database query to load it.
You can query EF's ChangeTracker, it's especially well supported with DbContext where you have access to the context cache via the DbSet<T>.Local collection.
The problem here is that there is no logic to query the database automatically if a query on Local does not return a result. You have to write this logic manually. The even bigger problem is that a query on Local is LINQ-to-Objects and not LINQ-to-Entities (Local doesn't implement IQueryable<T>, only IEnumerable<T>), so you often have to rewrite your queries to act on Local - for example you can't use Include here, your can't use any EntityFunctions, you will get different behaviour for string comparisons regarding case sensitivity, etc., etc.
Related
I've started using EF Core for the 1st time with asp.net core and I must confess that I'm not finding the experience that good (specially if you're coming from NH). Now, I've got the following scenario:
2 different DbContexts and each context uses different schemas
the API has been set up so that some API operation are wrapped by a transaction (in practice, everything has been set up so that there's a single sqlconnection that's shared by both contexts)
I'd like to test these kind of methods, but I'm not sure on the best approach...After reading the docs, it looks like the best option is using SQLite, but there's a small gotach: setting up the db.
The docs say that I can use the context.Database.EnsureCreated method. Unfortunately, that will only work if the in memory db hasn't been created yet. In other words, after calling if from the 1st context instance, it won't do anything when called over the 2nd context instance (because both context share the same db and it has already been created after the 1st call). In practice, this means that I'll end up with a partial db that has the tables mapped to the entities of the 1st context.
Is there a way to force the creation of the 2nd context tables? Ie, can I write something like this with EF Core:
contextA.Database.EnsureCreated();
contextB.Database.JustCreateTheTablesPlease();
Or do I need to recreate my db from a SQL script before running my tests?
Thanks.
I have a MVC4 web app that calls to a WebApi. The WebApi returns a JSON list of objects that are then converted to a appropiate Model and passed to the View.
Currently I am 'caching' this list of objects before its passed to the View in a Session variable that can then be paged through pagination.
I know this is bad. My idea is to store the JSON list in a database.
What I need to do is determine the best way to associate the a JSON item list in the database with the user that requested it. I was thinking of creating a Session variable each time a user first accesses the Controller of the app and then store the ID (based on a GUID) that can then be queried against the database.
Again the above idea requires the use of a Session which is bad. What could I use instead?
Please note I have no Login in system and therefore no 'Users' table so ID's will have to be assigned on the fly.
The data you keep in session will be available to any request from the current session until session expires.
Session is not always bad. There are some valid use cases where you can store some data in session provided you are not using the In-Memory session. Using In-Memory session is bad. You should use a small db table to store the data you wish to keep in session (Ex : Shopping cart data etc..) or if you really want session(why?), you should use StateServer or SqlServer session modes
If you do not wish to keep your app's local db to store this information, You may also consider keeping that in a caching layer instead of session. You may use the MemoryCache class available in dot net.
Quick sample of setting data to the cache.
ObjectCache cache = MemoryCache.Default;
CacheItemPolicy policy = new CacheItemPolicy();
policy.AbsoluteExpiration = DateTime.Now.AddDays(1);
var products = new List<string> { "iPhone","MacBookPro","Beer"};
var userId=25;
var cacheKey="productsOf"+userId;
cache.Set(cacheKey, products, policy);
And to read from the cache,
var userId=25;
var cacheKey="productsOf"+userId;
var test= cache.Get(cacheKey);
Get method returns an object and you need to explicitly cast it to your custom class/data structuer. You can keep any object in the cache. Based on your specific requirement update the expiration date of the cache. MemoryCache class is available in the System.Runtime.Caching namespace.
Similar to in-memory session, cache will be automatically cleared periodically. So your best bet is to use a small local db table i guess.
I use Fluent NHibernate code to create a MySQL database SessionFactory. No config files (just one value for the connection string in configuration - connectionStrings section of configuration file).
The SessionFactory creation code is contained in a Data tier class: SessionFactoryManager, which implements a singleton internal SessionFactory which is used by the Data and Business tiers to get all the sessions via SessionFactoryManager.OpenSession().
Some of my Business tier methods internally call SessionFactoryManager.OpenSession() to create sessions in a way that is transparent to the Presentation layer. So, when calling this methods there is no parameter or return value involving a session (to keep the Presentation layer "session-agnostic" when using those Business tier methods).
My problem comes when I write the integration tests for the Business layer: I would like to make them run on a SQLite in-memory database. I create a SessionFactoryManager which uses Fluent configuration to configure the SQLite database.
But when testing those methods that internally create the session, I can not tell them to use my testing SessionFactory (configured to use SQLite). So the "real" SessionFactory is called, and so the MySql database is used, not the SQLite.
I'm thinking of several possible solutions, but none of them seems right.
I could migrate the NHibernate configuration in Data layer to config files, and make different NHibernate config files for development/production and test environments, but I really would prefer to keep on with Fluent code.
I could also modify my Data layer to use a single configuration value, databaseMode or similar, that sets the database to be used: testing in-memory or the real one. And write some switch(databaseMode) statements like "case inMemory: { ... fluent code for in-memory SQLite... } case standard: { ... fluent code for standard database ... }". I don't like this approach at all, I don't want to modify my Data tier code functionality just for testing purposes.
Notice that I'm not testing Data layer, but Business layer. Not interested in testing NHibernate mappings, Dao or similar functionality. I already have unit tests for that, running OK with SQLite database.
Also, changing database is not a requirement of my application, so I'm not quite interested in implementing significant changes that allow me to dynamically change the DBMS, I only came to this need in order to write the tests.
A significant point: when using in-memory SQLite the database connection must be the same for all new sessions, otherwise the database objects are not available to the new sessions. So when creating a new session with SessionFactory.OpenSession() a parameter "connection" must be provided. But this parameter should not be used with non in-memory database. So the switch(databaseMode) should be used for any single session creation! Another Data layer code change that I don't like at all.
I'm seriously considering giving up and running my tests with the real database, or at least on an empty one, with its objects created and dropped for any test execution. But with this the test execution will surely be slower. Any ideas? Thanks in advance.
Finally my solution was Inversion Of Control: I changed my data tier so I can inject a custom SessionFactoryBuilder class that makes the Fluently.Configure(...) magic.
In my data tier I use the "real" MySqlSessionFactoryBuilder, in my test projects I write TestMySqlFactoryBuilder or TestSQLiteSessionFactoryBuilder classes, or whatever I need.
I still have problems with SQLite feature that requires that the same connection is used for all sessions, and must be passed as a parameter in every ISession.Open() call. By the moment I have not modified my data tier to add that feature, but I would like to do it in the future. Probably by adding to my SessionFactory singleton a static private member to store the connection used to make SchemaExport, and a static private boolean member like PreserveConnection to state that this connection must be stored in that private member and used in every ISession.Open(). And also wrap ISession.Open() and make sure that no session is opened directly.
Developing web site (using Entity Framework) i have encountered in following questions:
1.What happens if a lot (lets say 10,000) people trying "to write" simultaneously to the same specific table in DB (SQL Server) via Entity Framework ?
2.In my project i have modules and for decoupling reasons i using singleton class (ModulesManager) which should take Action from each module and execute it asynchronous like following:
public void InsertNewRecord(Action addNewRecordAction)
{
if (addNewRecordAction != null)
{
addNewRecordAction.BeginInvoke(recordCallback, null);
}
}
Is it good approach to use singleton class as only place responsible to write to DB ?
3.Does Entity Framework can provide same speed as using SQL queries ?
What happens if a lot (lets say 10,000) people trying "to write"
simultaneously to the same specific table in DB (SQL Server) via
Entity Framework ?
If you mean inserting to the same table those insert will be processed based on transaction isolation level in the database. Usually only single transaction can hold a lock for insertion so inserts are processed in sequence (it has nothing to do with EF). Having 10.000 users concurrently inserting doesn't seem like sustainable architecture - some of them may timeout.
In my project i have modules and for decoupling reasons i using
singleton class (ModulesManager) which should take Action from each
module and execute it asynchronous like following:
Your manager asynchronously invokes the action so the answer is mostly dependent on what the action is doing. If it opens its own context, performs some changes and saves them, you should not have any technical problem on EF side.
Does Entity Framework can provide same speed as using SQL queries ?
No. EF does additional processing so it will always be slower.
In reading an article on N-Tiered Applications, I came across information regarding concurrency tokens and change tracking information:
Another important concept to understand is that while the
default-generated entities support serialization, their
change-tracking information is stored in the ObjectStateManager (a
part of the ObjectContext), which does not support serialization.
My question is three-fold:
Is there the same thing when using DbContext?
If the only interaction with the database is in a Repository class within a using statement, does closing the database connection when the program leaves the using statement get rid of any option for change tracking?
Can this be leveraged as/with a Concurrency Token?
Yes. DbContext is just wrapper around ObjectContext and it exposes change tracking information through ChangeTracker property (returns DbChangeTracker) and for particular entity through calling Entry method (returns DbEntityEntry<T>).
Yes. Closing context will remove all change tracking information.
Concurrency token and change tracking are two completely different concepts. Change tracking tells context what operations it has to execute on database when you call SaveChanges. It tracks changes you did on your entities since you loaded them into current context instance. Concurrency token resolves optimistic concurrency in the database => it validates that another process / thread / user / context instance didn't change the same record your context is going to modify during SaveChanges.