Author |
Share Topic Topic Search Topic Options
|
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Topic: Entity manager cache not working Posted: 07-Nov-2012 at 8:22pm |
Perhaps you are wondering (a) Why did the query return results but no entities in cache?
(b) Why did Breeze allow the model type and DbContext namespaces to be mismatched? Why didn't it protest?
Let me assure you that Breeze behaved exactly as intended ... if not in the way you expected.
In this example, the client query was routed to the Web API controller's "Items" method. The server responded with JSON data for a type 'Item" belonging to the namespace 'Model'. Breeze on the client doesn't know about an 'Item' type in the namespace 'Model'; it knows about an 'Item' type in the namespace 'Data'. But that's not the same type at all. Breeze simply doesn't know what type of data it received. Therefore, it cannot turn that data into 'Item' entities ... which is why there are no Items in cache.
But the server did return data and Breeze dutifully provides those data as JavaScript objects in the 'data.results' array of the query promise. In the post which started this thread, we know the server returned 45 'somethings'. They weren't entities - Breeze didn't know what they were. But you, the developer, asked for them and here they are.
Breeze does not demand that a query return entities. If the service returns data that it recognizes as entities - if the data are identified in metadata as entity types - Breeze will turn those data into entities and will put them in cache. Otherwise it forwards them to the caller via the promise.
There is nothing wrong with the server sending arbitrary data to the client. That's actually a useful feature. For example, suppose you have a Person entity and Person has 100 columns/properties including an image property that could be 100KB. We want to present a list of Persons, just their first and last names. We can't afford to download every property of every Person in the list.
Well, in Breeze we can make a "projection query" - a query for selected properties - and transmit only the properties we need. Clearly the data objects that satisfy our query are not whole Person entities. They are something else, something unnamed and un-typed. But something useful nonetheless.
Breeze doesn't judge. It doesn't insist that the query return whole Person entities. It simply examines the incoming data. If they are Persons, they become entities in cache. If not, fine, pass them along to the caller.
Therefore, Breeze shouldn't protest when it receives a data object of an unrecognized type. It was prepared to recognize 'Data.Item' data; it received 'Model.Item" data; no big deal ... and no entities in cache either.
---
This isn't the entire story. Actually, Breeze examines the incoming data objects looking for entities. If the incoming data are object graphs, Breeze traverses the graphs, looking for objects which it recognizes as entities. If it finds entities within the object graphs, it puts them in cache.
This is a super valuable feature. I use it to query and cache all of my pick-lists in a single shot. For example, I might create a service method called "Lookups" that returns a single object whose properties are arrays of Color, Status, Size, ProductType, ... you get the idea. That object is essentially a bag of lists that I'll use to populate comboboxes.
Then I make a single query to "Lookups" ... which returns this bag of lists. Now breeze doesn't recognize the bag at all. But each of the bag's properties is a a collection of objects that are described in metadata: Color is an entity type, Status is a type, Size is a type, ProductType is a type. Breeze recognizes that these nested objects are entities and puts them in cache.
So in a single request, in a single payload, I'm able to populate the EntityManager cache with all of the little pick-lists.
That's pretty cool.
Edited by WardBell - 07-Nov-2012 at 8:29pm
|
|
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 5:59pm |
Just so we're clear: classes in different projects can share the same namespace. This happens all the time; many Microsoft classes in different assemblies share the same namespace.
You may not want to do that but you certainly can.
If you don't want to do that, the Entity Framework DbContext documentation shows a simple workaround: create a subclass of the DbContext that is in the model's namespace. The DbContext subclass doesn't have to do anything other than inherit from your base DbContext ... and shift the namespace. This, I think, is a small price to pay to trick the metadata generation tooling.
I agree with separating the projects as you have done. That can be wise as models grow. Breeze will not interfere with that separation. You just need to spit on it a little bit to get the namespaces aligned.
-- The above is in reply to CCPony who wrote the following ... which somehow has disappeared (maybe I deleted by accident?) -- Placing the data components and the model files into
separate projects and referencing those projects in the main web project causes
the error.
For example, if you have the following three projects:
BreezeTest.Model - contains item.cs
BreezeTest.Data - contains BreezeTestDatabaseInitializer.cs and
BreezeTestDbContext.cs. This projects references BreezeTest.Model
BreezeTest.Web references both BreezeTest.Model and BreezeTest.Data
When you run BreezeTest.Web in this configuration you get: "3 items in
query" and "0 entities in cache".
As per Ward's attached sample, however, when all the files in the one project,
you get "3 items in query" and "3 entities in cache".
So, the problem seems to be limited when using separate projects. By the way, I
used the RequireJs attachment.
Let me know if you have any other questions.
Edited by WardBell - 07-Nov-2012 at 6:08pm
|
|
svk
Newbie
Joined: 11-Oct-2012
Posts: 7
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 5:52pm |
Sorry for the oversight... I previously read through all of the documentation and I must have overlooked this. It probably wasn't relevant at the time I read it. This is not a show stopper for me so I am happy to move on. thanks again.
|
|
CCPony
Newbie
Joined: 07-Nov-2012
Location: Maryland
Posts: 3
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 5:44pm |
I did some testing just came to the same conclusion. Thanks for handling so quickly.
Edited by CCPony - 07-Nov-2012 at 6:04pm
|
|
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 3:13pm |
Yes, that is a known and documented requirement. The Model classes and the DbContext(s) must be in the same namespace; the controller can be in a different namespace.
We won't be trying to "fix" this any time soon. The Entity Framework tooling we use to generate the metadata only supports a single shared namespace and we don't think building our own metadata generation is a good investment at this time.
This issue ... and a workaround ... are discussed in our documentation on using the Entity Framework DbContext in a Breeze application.
After you've looked at that, if you still feel this is a critical shortcoming, please let us know and explain why.
|
|
svk
Newbie
Joined: 11-Oct-2012
Posts: 7
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 2:56pm |
Thanks for the sample projects, Ward. I was able to identify the problem. Simply put, Breeze requires that the models and dbcontext be in the same namespace.
You can repro the described behavior in your projects by moving BreezeTestDbContext & BreezeTestDatabaseInitializer into a different namespace.
Is this something that can easily be fixed? Thanks!
|
|
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 2:10pm |
Now that I know how to attach a file, I present you with these same zip files attached to the forum post itself.
If you can reproduce the problem using either of these solutions and want us to evaluate it, you can upload your solution as I did here..
|
|
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 12:29pm |
I'm old around here and I don't see them either. Some problem with the forum attachment mechanism.
For now, you can get them at
Until we get forum attachment straightened out, you'll have to tell us where to get the repro solutions that you create.
|
|
CCPony
Newbie
Joined: 07-Nov-2012
Location: Maryland
Posts: 3
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 12:21pm |
I'm new around here. Ward, I don't see your attachments. Where are they? Thanks.
|
|
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 12:02pm |
Ok everyone ... I cannot reproduce your issues. But I can give you two solutions, one with Require and one without, that follow the path prescribed by svk. I've attached both.
If you want to press this issue productively, I suggest that you use either of these solutions as a baseline for your reproduction of the error.
|
|
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 11:28am |
I have a test bed that matches SVK's sample ... except it doesn't yet use RequireJS. It works fine. Will now create a second version that uses Require. Maybe that is introducing a problem ... although we use Require all over the place.
|
|
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 9:57am |
Hang in there. We'll take SVK's sample and try to repro. With luck everything we need will be there ... or at least we'll have a shared solution to kick around.
|
|
CCPony
Newbie
Joined: 07-Nov-2012
Location: Maryland
Posts: 3
|
Post Options
Quote Reply
Posted: 07-Nov-2012 at 9:35am |
I'm currently experiencing this exact same problem.
Edited by CCPony - 07-Nov-2012 at 9:37am
|
|
jtraband
IdeaBlade
Joined: 19-Sep-2012
Posts: 55
|
Post Options
Quote Reply
Posted: 06-Nov-2012 at 10:36am |
Looking at it now.
|
|
svk
Newbie
Joined: 11-Oct-2012
Posts: 7
|
Post Options
Quote Reply
Posted: 06-Nov-2012 at 8:57am |
Any thoughts? Am I doing something incorrectly or might I be running up against a bug? Thanks.
|
|
svk
Newbie
Joined: 11-Oct-2012
Posts: 7
|
Post Options
Quote Reply
Posted: 04-Nov-2012 at 8:04am |
Using a very simple test application, I am experiencing the same problem - manager.getEntities().length always returns 0 while data.results.length returns the actual number of entities. Below are the components of the test app. Please let me know if I can provide any more information or if you have any ideas. Thanks!
Index.chtml looks like this: <!DOCTYPE html> <html> <head> <title>Breeze Test</title> </head> <body> Test <script src="/Scripts/jquery-1.8.2.js"></script> <script src="/Scripts/knockout-2.2.0.debug.js"></script> <script src="/Scripts/q.js"></script> <script src="/Scripts/breeze.debug.js"></script> <script src="/Scripts/require.js"></script>
<script src="/Scripts/app/main.js"></script>
</body> </html>
Note: knockout is loaded but not used. The results are the same even when the test is expanded and a ko array is populated. The array is filled with entities but manager.getEntities() returns 0 so executeQueryLocally doesn't work.
main.js boots the app and looks like this: (function () {
configRequire(); boot();
function configRequire() { requirejs.config({ paths: {'breeze': 'Scripts/breeze.debug'} }); }
function boot() {
require(['breeze'], function (breeze) { var core = breeze.core, entityModel = breeze.entityModel;
core.config.setProperties({ trackingImplementation: entityModel.entityTracking_ko, remoteAccessImplementation: entityModel.remoteAccess_webApi });
var serviceName = 'api/breezetest'; var manager = new entityModel.EntityManager(serviceName);
var query = new entityModel.EntityQuery().from("Items");
manager.executeQuery(query) .then( function (data) { alert("Got " + data.results.length + " data rows"); alert("Got " + manager.getEntities().length + " entities"); }) });
} })();
controller: namespace BreezeTest.Controllers { public class BreezeTestController : ApiController { readonly EFContextProvider<BreezeTestDbContext> _contextProvider = new EFContextProvider<BreezeTestDbContext>();
[AcceptVerbs("GET"), ActionName("Metadata")] public string Metadata() { return _contextProvider.Metadata(); }
[AcceptVerbs("GET"), ActionName("Items")] public IQueryable<Item> Items() { return _contextProvider.Context.Items; } } }
metadata: {"conceptualModels":{"schema":{"namespace":"BreezeTest.Data","alias":"Self","d4p1:UseStrongSpatialTypes":"false","xmlns:d4p1":"http://schemas.microsoft.com/ado/2009/02/edm/annotation","xmlns":"http://schemas.microsoft.com/ado/2009/11/edm","entityType":{"name":"Item","key":{"propertyRef":{"name":"Id"}},"property":[{"name":"Id","type":"Edm.Int32","nullable":"false","d4p1:StoreGeneratedPattern":"Identity"},{"name":"Name","type":"Edm.String","fixedLength":"false","maxLength":"Max","unicode":"true","nullable":"true"},{"name":"Description","type":"Edm.String","fixedLength":"false","maxLength":"Max","unicode":"true","nullable":"true"}]},"entityContainer":{"name":"BreezeTestDbContext","entitySet":{"name":"Items","entityType":"Self.Item"}}}}}
model: namespace BreezeTest.Models { public class Item { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; }
} }
dbcontext: namespace BreezeTest.Data { public class BreezeTestDbContext : DbContext { public DbSet<Item> Items { get; set; } } }
|
|