In my application I have an "Event". And to this Event, I can attach documents.
Now when I show a dialog of this Event, I want to list all the documents, that are related and I do this with a "select"-Projection (because I don't want the property in the document, that has the binary representation in it, which would be huge).
Now from this list, I should be able to delete a document. As I did a projection, I don't have a breeze entity for the document so I cannot call setDeleted() on the entityAspect.
How would this be done? Do I have to query the whole document just to delete it?
I do all of this in a sandboxed DataContext so just sending a command to the server to delete the document and requery is not an option - as the dialog can be cancelled.
If I do this with standard queries and navigation properties everything works fine but potentially slow if there are big images.
Check out this answer. Essentially, you can create an entity with the id of the entity you want deleted and set the entityaspect to 'deleted' and that should take care of it.
Related
If I use breeze to load a partial entity:
var query = EntityQuery.from('material')
.select('Id, MaterialName, MaterialType, MaterialSubType')
.orderBy(orderBy.material);
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
var list = partialMapper.mapDtosToEntities(
manager, data.results, entityNames.material, 'id');
if (materialsObservable) {
materialsObservable(list);
}
log('Retrieved Materials from remote data source',
data, true);
}
...and I also want to have another slightly different partial query from the same entity (maybe a few other fields for example) then I'm assuming that I need to do another separate query as those fields weren't retrieved in the first query?
OK, so what if I want to use the same fields retrieved in the first query (Id, Materialname, MaterialType, MaterialSubType) but I want to call those fields different names in the second query (Materialname becomes just "name", MaterialType becomes "masterType" and so on) then is it possible to clone the partial entity I already have in memory (assuming it is in memory?) and rename the fields or do I still need to do a completely separate query?
I think I would "union" the two cases into one projection if I could afford to do so. That would simplify things dramatically. But it's really important to understand the following point:
You do not need to turn query projection results into entities!
Backgound: the CCJS example
You probably learned about the projection-into-entities technique from the CCJS example in John Papa's superb PluralSight course "Single Page Apps JumpStart". CCJS uses this technique for a very specific reason: to simplify list update without making a trip to the server.
Consider the CCJS "Sessions List" which is populated by a projection query. John didn't have to turn the query results into entities. He could have bound directly to the projected results. Remember that Knockout happily binds to raw data values. The user never edits the sessions on that list directly. If displayed session values can't change, turning them into observable properties is a waste of CPU.
When you tap on a Session, you go to a Session view/edit screen with access to almost every property of the complete session entity. CCJS needs the full entity there so it looks for the full (not partial) session in cache and, if not found, loads the entity from the server. Even to this point there is no particular value in having previously converted the original projection results into (partial) session entities.
Now edit the Session - change the title - and save it. Return to the "Sessions List"
Question
How do you make sure that the updated title appears in the Sessions List?
If we bound the Sessions List HTML to the projection data objects, those objects are not entities. They're just objects. The entity you edited in the session view is not an object in the collection displayed in the Sessions List. Yes, there is a corresponding object in the list - one that has the same session id. But it is not the same object.
Choices
#1: Refresh the list from the server by reissuing the projection query. Bind directly to the projection data. Note that the data consist of raw JavaScript objects, not entities; they are not in the Breeze cache.
#2: Publish an event after saving the real session entity; the subscribing "Sessions List" ViewModel hears the event, extracts the changes, and updates its copy of the session in the list.
#3: Use the projection-into-entity technique so that you can use a session entity everywhere.
Pros and Cons
#1 is easy to implement. But it requires a server trip every time you enter the Sessions List view.
One of the CCJS design goals was that, once loaded, it should be able to operate entirely offline with zero access to the server. It should work crisply when connectivity is intermittent and poor.
CCJS is your always-ready guide to the conference. It tells you instantly what sessions are available, when and where so you can find the session you want, as you're walking the halls, and get there. If you've been to a tech conference or hotel you know that the wifi is generally awful and the app is almost useless if it only works when it has direct access to the server.
#1 is not well suited to the intended operating environment for CCJS.
The CCJS Jumpstart is part way down that "server independent" path; you'll see something much closer to a full offline implementation soon.
You'll also lose the ability to navigate to related entities. The Sessions List displays each session's track, timeslot and room. That's repetitive information found in the "lookup" reference entities. You'll either have to expand the projection to include this information in a "flattened" view of the session (fatter payload) or get clever on the client-side and patch in the track, timeslot and room data by hand (complexity).
#2 helps with offline/intermittent connectivity scenarios. Of course you'll have to set up the messaging system, establish a protocol about saved entities and teach the Sessions List to find and update the affected session projection object. That's not super difficult - the Breeze EntityManager publishes an event that may be sufficient - but it would take even more code.
#3 is good for "server independence", has a small projection payload, is super-easy, and is a cool demonstration of breeze. You have to manage the isPartial flag so you always know whether the session in cache is complete. That's not hard.
It could get more complicated if you needed multiple flavors of "partial entity" ... which seems to be where you are going. That was not an issue in CCJS.
John chose #3 for CCJS because it fit the application objectives.
That doesn't make it the right choice for every application. It may not be the right choice for you.
For example, if you always have a fast, low latency connection, then #1 may be your best choice. I really don't know.
I like the cast-to-entity approach myself because it is easy and works so well most of the time. I do think carefully about that choice before I make it.
Summary
You do not have to turn projection query results into entities
You can bind to projected data directly, without Knockout observable properties, if they are read-only
Make sure you have a good reason to convert projected data into (partial) entities.
CCJS has a good reason to convert projected query data into entities. Do you?
So far all of the examples that I have seen for adding a new entity would go through the following steps:
Create a bunch of "new-" variables that get bound to on screen controls.
When user wants to submit the addition, created an uninitialized new entity.
Copy "new-" variables to each member of the new entity one by one.
Push the new entity onto the manager's entity list.
Save changes.
Clear all of the "new-" variables.
This is problematic for many reasons. These "new-" variables have to be maintained in addition to the on screen controls. When server side entities change, they must be changed manually. This is time consuming and error prone.
I would like to be able to create an uninitialized new entity first, and bind it to on screen controls immediately, without using those variables with the "new-" prefix. When user wants to submit the addition, push the new entity onto the manager's entity list, and then save changes. Then bind the on screen controls immediately to a newly created uninitialized entity.
This way, we can avoid dealing with individual entity attributes in the view model, which would produce more robust code, and save a lot of time.
Right now, the metadata for creating a new entity is not available when the document becomes ready. If I download it, I have to deal with asynchronous completion before I can bind a new entity to on screen controls.
So my question is: is there a way to have the metadata downloaded with the initial HTML download so that I can create an uninitialized new entity without waiting, and bind it to on screen HTML controls immediately?
I'm not sure what means uninitialized new entity, but yes - you can create for example a new car like this:
var newCar = manager.metadataStore.getEntityType("Car").createEntity();
manager.addEntity(newCar);
Bind your controls to the newCar. To save the changes call manager.saveChanges();, to cancel call manager.rejectChanges();
To be able to work with entities breeze needs metadata. If you want, you can send metadata with the page itself. You can then use manager.importMetadata() to import it into the manager. The only problem is that you will need to write a small app that will generate the metadata string (during build) so that later it can be passed to importMetadata(). Take a look at MetadataStore Class API for more info.
I'm using Entity Framework with an AS.NET MVC application. I need to allow the user to create new records and modify existing ones. I am able to fetch existing records no problem, but when I pass back in the edited entity and try to save it it creates a new one and saves it and leaves the original unmodified.
I am getting the object from EF using the primary key (e.g. ID number for an employee record). I successfully retrieve it, and set the MergeOption like so:
Context.Sector.MergeOption = MergeOption.NoTracking;
I am able to trace that the object has the correct data (using the key of the original record) all the way down to the point where I call:
Context.SaveChanges();
However, after that, the new record is created instead of modifying the existing one.
Is there something obvious I am missing here? I would have thought that retrieving the object and changing some of its values (not the ID) and saving it would just work, but obviously not.
Thanks,
Chris
"NoTracking means that the ObjectStateManager is bypassed and therefore every access to the Entity Objects results in a fetch from the database and the creation of new objects."
-- http://blog.dynatrace.com/2009/03/11/adonet-entity-framework-unexpected-behaviour-with-mergeoptions/
I don't think NoTracking is what you want.
From your comment: "distributed across various tiers and some proprietary libraries"
Are you new()ing up a ObjectContext, closing it or losing the reference to it, and then trying to save your object to a new() or different ObjectContext?
If so your losing all of your change tracking information. If this is the case then you want to call the Attach() method to reattach the entity to the context, ApplyPropertyChanges() and then finally SaveChanges().
Julie Lerman has a pretty good blog post that outlines all the different change tracking options and techniques that are available. You should also check out this MSDN article on the same subject.
I'm coming from the delphi world and I want to make a master/detail interface, like Order and Products.
I already made actions to display the data using fields and a jqGrid. What I want know is how make possible to add lines, edit or remove them, but, just make the changes in db when the user confirm the changes in the master.
On delphi I would use a TClientDataSet with all the in memory changes and just after the confirmation would execute them inside a transaction like:
BEGIN
Master.Post
FOREACH Line IN Lines Line.Post
COMMIT
So in resume, I don't know how keep in memory the array of lines in the grid and how send them back to server to commit.
Any help will be appreciated. Thanks in Advance.
You'll need to keep track of the changes client side, perhaps using some hidden fields and/or form fields in your grid. When a line is deleted (that previously existed in the db), you'll need to add it's id to a field containing lines to delete. Lines that are added need to have associated form fields containing their data. When the master is committed you roll the whole set of fields up into a POST and send that back to the server.
Using LINQ to SQL, you'd create a data context, get the master object, then delete the related objects (from the hidden field of ids) that are so marked and create/add new related objects that didn't exist before taking the values from the appropriate form fields. Then you'd do a SubmitChanges and all of the statements would be executed within a single transaction.
Here's my question:
I need to write a wizard, for customers to "create a new" very big objetc, with some other asociated with it: for example, Some images stored in another table (with relationships), some Lat's and Lang's for google earth, etc.
Each of them are stored in diferent tables in the Database, and that's why, i have to first insert to get the first object's Database generated ID to make the relationships with the another Objects. That's the reason I think puttin' Everything on just one View and hide selective DIVs with Jquery is not one of my option.
Session isn't an option because of the bigger object.
And because of the type of website, the wizard MUST be as follows:
Basic details of objetct 1
Images of object 1 (I will need here the ID of the first object)
Geolocations (with google maps, as before)
More details of object 1.
Preview
Publish
The point is, in step 4, user fill some fields that are required by the DB, and I cannot make them nullable as is it part of the customers reqs.
If somebody can a least give Ideas, will be nice...
Thanks in advance
You state that storing your object in Session is not desirable because of the size of the object. An alternative is to serialize that object and store it in the database. As the user progresses through the wizard, that object gets retrieved, updated and stored back in as a blob. Once they publish it, you can insert the appropriate records and remove the serialized object from whatever table you're storing them in.