Having an entity in cache, that entity has related entities in the form of a top 20.
Now a user action can update the top 20 on the server, and I thus would like to redownload the entire entity. Server sends the correct data with top 20, but in Breeze, I end up with a top 40... And I can't figure out how to avoid this behavior.
Thanks for the tip
Update: I do not use odata webapi and iqueryable, as it offers too much power to clients for my app. So I don't want to use EntityQuery.fromEntityKey, which seems to do what I want. I'd prefer to keep using a "normal" query, to which I add a parameter.
Update 2: To add more clarity as to why I want to prevent merge, when I recompute the top 20, I delete all related entries in the db and recreate them, so they have new Id's. So I am now considering an update, which might actually solve my issue BUT I would still like to know if merge can be prevented.
The Breeze EntityManager caches entities by primary key. So presumably your 2nd query is returning a completely new set of entities with each query. If this is the case, and you really only want the "latest" 20, the simplest fix would be to simply empty the EntityManager cache for this entity type before each query. Something like:
var entities = myEntityManager.getEntities(myEntityType);
entities.forEach(function(e) {
myEntityManager.detachEntity(e);
// or
// e.entityAspect.setDetached();
});
I am not sure what is exactly your qustion, but if you want to have different results on entity queries you may do it by creating 2 different controllers with different action names, or just with different parameters - overloaded methods. Then in your datacontext you can have 2 different queries.
You use .withParameters() propriety on the query to call the controller method with parameters.
Then in the controller method you can query and filter with LINQ, in any way you want. This way you can have different results based on the query/controller you chose to call.
Documentation: http://www.breezejs.com/documentation/querying-depth
Related
I'm having a weird (or at least unexpected) problem with Breeze. I have an EF back-end view model to which I send some parameters and it returns some data. I use the withParameters option to do this. The back-end does a lot of Includes and projection and returns the data I want to display in a set of custom view model entities (i.e., not database entities). One of the parameters is a list of keys for which I want to display data.
The keys identify which children of the parent entities I want to retrieve, though I am retrieving a list of parents (e.g. keys [1,2] mean it should get all Parent entities with a Children list property that themselves have a ToyId property that has a value in keys and those Child entites). In other words, the structure is like Parent.Child[] and Child.ToyId and I want to get parents with children that have certain toys and those children themselves (but not other children). Both parent and child sets are large so I do this in SQL via EF (which was an adventure in itself).
Anyway, the problem happens after I select two keys and get the data and then de-select one of the keys. The first query, getting the data for two keys, works as expected. On the second executeQuery's callback, I get the same data as the previous query, meaning it's as if I never de-selected the key. I've verified that Breeze hits the back-end with the correct keys parameter value and the back-end returns just the data I want, but it seems that Breeze is ignoring the data from the back-end or performing a union on the result set from the back-end and its cached entities (for both keys) and sending that union as results into the callback instead of just what the server returned. Is this expected behavior? Unfortunately everything is written this way. We (working on this, our first project using Breeze) all assumed it would only return what the server sent when not using executeQueryLocally, so it will be a big deal to refactor. Sigh.
I tried some where predicates which didn't work and don't see how projecting on the Breeze side would help either. I thought maybe it saw the query as identical so it returned cached data as a shortcut, so I added a where('Parent.Children', 'any', 'ToyId', 'in', keys), but that didn't work, it still brings in the de-selected results.
The only way I've found to get around this is via queryManager.clear() before I make any of the queries, and I suspect doing a noTracking query might work also (albeit without actual entity objects). I thought about converting the keys parameter into a where filter and sourcing it from there instead of the keys parameter, in case that would tell Breeze to only show the back-end data.
Is there a "correct" way of getting back only the data the server sends in the callback?
("The callback" meaning the function passed into executeQuery.then(...))
As you've noticed, Breeze merges the query results into the cache before calling your callback method. This means that, in the callback, a parent entity will have all of its children that exist in the cache.
The array of entities returned by the query is available in the retrievedEntities property of the saveResult object returned in the callback. You can use this to determine which children were returned from the query.
entityManager.executeQuery(query).then(function(data) {
var parents = data.results;
var ret = data.retrievedEntities;
//... filter ret to get the children
//... update viewmodel with children
});
I have a n-tier solution with these projects in it (simplified for this question):
Domain
Logic
Web
In the "Domain" project I have a "Repositories" namespace and each repository is mapped to a different table in the DB and query its data.
For example - the tables Customers and Orders will have the corresponding repositories - CustomersRepository and OrdersRepository.
In the Logic project I instantiate these repository objects and call their methods which actually query the DB.
Lets say I want to show a report that display some data from both tables.
This report is constructed by a collection of custom objects - IList<ReportObject>.
Now, this ReportObject object has no corresponding table in the DB and therefore has no repository object.
My question: Where should I put the part of code that actually query the DB and fetch IList<ReportObject>? Should it just be in some data controller in the Logic layer? or create another repository for the reports? Any other option?
While I think this is mainly a question of opinion, here goes:
You can create a QueryStore<ReportObject> instead of a Repository<ReportObject>. The name QueryStore is just something I came up with, it's not a coined term.
The function of such a query store would be to, well, run queries on data that is not covered by any repository. It would contain only queries and so can, for instance, easily be implemented using LINQ on top of Entity Framework querying database VIEWs for instance.
I'd put it in a custom repository (as this is not a CRUD operation). You can extend Repository (if you're working with a generic repository) and create one for the query. I wouldn't put queries in other place rather than Repositories since you'll brake the encapsulation of what Repository does. Imagine you change the database in the future, it won't be enough to change the repositories layer. Another reason to not put it there is that the logic will be spread around the application instead of being everything in just one place which simplifies debugging and improvements in the queries.
Hope it helps. Guillermo.
The repository pattern is used to encapsulate CRUD operations, but in your case you do not need any Insert or Update. I would put this into the Logic layer and access the DB directly from there.
I have something like a Customer object with up to 50000 order in an ICollection<Orders>.
Assume the Custome being in the local cache, the orders not. How can i delete the Cutomer and all of its related orders without loading all of the Customer orders into the cache and marking them with setDeleted()?
What is the best practice here. I assume extending the public SaveResult SaveChanges(JObject saveBundle) method is the best way. Any other possibilities here on the client side like a flag delete_all_navigation_too()?
Thanks
I must suppose that you do not have and do not want cascade delete on your database. Personally, I'm "terrified" of deletes in general and try to avoid them. I prefer a soft delete (marking a record as inactive). But not everyone agrees or can follow suit
I would consider adding a Web API method (say "DeleteCustomerAndOrders") to your controller to do it. You can call any API method from your client, not just a Breeze method.
In recommending this, I'm assuming that this kind of thing is a relative rarity in your app. You don't need a general purpose deleter, a deleter that takes an array of parent object IDs, a deleter that will delete some child objects and not others, ... etc., etc.
Follow this path and you will have moved the problem from the client to the server. That's good: you didn't have to load the orders on the client. Now you have to get rid of them on the server. If you're using Entity Framework, you face the same challenge of deleting the orders without loading them. Check out Alex James' solution: Bulk-deleting in LINQ to Entities.
Simplest approach that I can come up with is to create a cascade delete constraint on the database so that when a customer is deleted all of its orders get deleted as well. Then simply delete the customer on the client and call 'SaveChanges'. In addition, since Breeze does not yet support client side 'cascaded' deletes ( we are considering this one), you will need to iterate over any client side orders that are already loaded and 'detach' them.
I am quite sure I've seen the answer to this question somewhere, but as I couldn't find it with a couple of searches on SO or google, I ask it again anyway...
In Entity Framework, the only way to delete a data object seems to be
MyEntityModel ent = new MyEntityModel();
ent.DeleteObject(theObjectToDelete);
ent.SaveChanges();
However, this approach requires the object to be loaded to, in this case, the Controller first, just to delete it. Is there a way to delete a business object referencing only for instance its ID?
If there is a smarter way using Linq or Lambda expressions, that is fine too. The main objective, though, is to avoid loading data just to delete it.
It is worth knowing that the Entity Framework supports both to Linq to Entities and Entity SQL. If you find yourself wanting to do deletes or updates that could potentially affect many records you can use the equivalent of ExecuteNonQuery.
In Entity SQL this might look like
Using db As New HelloEfEntities
Dim qStr = "Delete " & _
"FROM Employee"
db.ExecuteStoreCommand(qStr)
db.SaveChanges()
End Using
In this example, db is my ObjectContext. Also note that the ExecuteStoreCommand function takes an optional array of parameters.
I found this post, which states that there really is no better way to delete records. The explanation given was that all the foreign keys, relations etc that are unique for this record are also deleted, and so EF needs to have the correct information about the record. I am puzzled by why this couldn't be achieved without loading data back and forth, but as it's not going to happen very often I have decided (for now) that I won't bother.
If you do have a solution to this problem, feel free to let me know =)
Apologies in advance, but I have to question your goal.
If you delete an object without ever reading it, then you can't know if another user has changed the object in between the time you confirmed that you wanted to delete the object and the actual delete. In "plain old SQL", this would be like doing:
DELETE FROM FOO
WHERE ID = 1234
Of course, most people don't actually do this. Instead, they do something like:
DELETE FROM FOO
WHERE ID = 1234
AND NAME = ?ExpectedName AND...
The point is that the delete should fail (do nothing) if another user has changed the record in the interim.
With this, better statement of the problem, there are two possible solutions when using the Entity Framework.
In your Delete method, the existing instance, compare the expected values of the properties, and delete if they are the same. In this case, the Entity Framework will take care of writing a DELETE statement which includes the property values.
Write a stored procedure which accepts both the IDE and the other property values, and execute that.
There's a way to spoof load an entity by re-calculating it's EntityKey. It looks like a bit of a hack, but might be the only way to do this in EF.
Blog article on Deleting without Fetching
You can create an object with the same id and pass it through to the delete
BUT its good for simple objects if you have complex relations you may need more than that
var user = new User { ID = 15 };
context.Entry(user).State = EntityState.Deleted;
context.SaveChanges();
var toDelete = new MyEntityModel{
GUID = guid,
//or ID = id, depending on the key
};
Db.MyEntityModels.Attach(toDelete);
Db.MyEntityModels.DeleteObject(toDelete);
Db.SaveChanges();
In case your key contains multiple columns, you need to provide all values (e.g. GUID, columnX, columnY etc).
Have also a look here for a generic function if you feel for something fancy.
With Entity Framework 5 there is Entity Framework Extended Library. Available on NuGet. Then you can write something like:
context.Users.Delete(u => u.Id == id);
It is also useful for bulk deletes.
This is a little out there but I have a customer object coming back to my controller. I want to just reconnect this object back to the database, is it even possible? I know there is a datacontext.customers.insertonsubmit(customer), but is there the equivalent datacontext.customers.updateonsubmit(customer)???
This is what I don't like about LINQ-to-SQL.
It generally works fine if you're querying and updating in the same scope, but if you get an object, cache it, and then try to update it later, you can't.
Here's what the documentation says:
Use the Attach methods with entities that have been created in one DataContext, and serialized to a client, and then deserialized back with the intention to perform an update or delete operation. Because the new DataContext has no way of tracking what the original values were for a disconnected entity, the client is responsible for supplying those values. In this version of Attach, the entity is assumed to be in its original value state. After calling this method, you can then update its fields, for example with additional data sent from the client.
Do not try to Attach an entity that has not been detached through serialization. Entities that have not been serialized still maintain associations with deferred loaders that can cause unexpected results if the entity becomes tracked by a second data context.
A little ambiguous IMHO, specifically about exactly what it means by "serialized" and "deserialized".
Also, interestingly enough, here's what it says about the DataContext object:
In general, a DataContext instance is
designed to last for one "unit of
work" however your application defines
that term. A DataContext is
lightweight and is not expensive to
create. A typical LINQ to SQL
application creates DataContext
instances at method scope or as a
member of short-lived classes that
represent a logical set of related
database operations.
So, DataContexts are intended to be tightly scoped - and yet to use Attach(), you have to use the same DataContext that queried the object. I'm assuming/hoping we're all completely misunderstanding what Attach() is really intended to be used for.
What I've had to do in situations like this is re-query the object I needed to update to get a fresh copy, and then do the update.
The customer that you post from the form will not have entity keys so may not attach well, also you may not have every field of the customer available on the form so all of it's fields may not be set.
I would recommend using the TryUpdateModel method, in your action you'll have to get the customer from the database again and update it with the form's post variables.
public ActionResult MySaveAction(int id, FormCollection form)
{
Customer updateCustomer = _Repository.GetCustomer(id);
TryUpdateModel(updateCustomer, "Customer", form);
_Repository.Save(updateCustomer);
}
You will have to add in all your own exception handling and validation of course, but that's the general idea.
You want to use the attach method on the customers table on the data context.
datacontext.customers.Attach(customer);
to reconnect it to the data context. Then you can use SubmitChanges() to update the values in the database.
EDIT: This only works with entities that have been detached from the original data context through serialization. If you don't mind the extra call to the database, you can use the idiomatic method in ASP.NET MVC of retrieving the object again and applying your changes via UpdateModel or TryUpdateModel as #Odd suggests.