I'm trying to duplicate a SPFieldDateTime's value into another SPFieldDateTime in an event handler and I have to admit, I'm stumped. There are no obvious fields I can set in an SPFieldDatetime and the following doesn't change my field's value:
{
SPListItem task = (SPListItem) properties;
task[/* destination field's guid */] = task[/* source field's guid */];
}
The code seems to be able to retrieve the fields without error. Using either of the GUIDs in SPFieldDateTime time = (SPFieldDateTime)task.Fields[/* either GUID */]; executes without error and the debugger appears to have the right field: the proper values exist in the properties etc.
How do I set a SPFieldDateTime value?
The failure in the above is that I did not call Update in this execution sequence. I did call update on the task but it is done in a privileged execution area which is isolated from the space the event handler runs in.
The fix:
{
SPListItem task = (SPListItem) properties;
task[/* destination field's guid */] = task[/* source field's guid */];
task.Update();
}
Related
After writing the content of the text file to the TStringList object, I try to change one character in the line and then write the entire content of the object back to the file. In the saved file, the changed line character was not saved as expected and is still unchanged. To write the change of character to the file I need to use the String object.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TStringList *Lista = new TStringList;
if(OpenDialog1->Execute())
Lista->LoadFromFile(OpenDialog1->FileName);
Lista->Strings[0][1] = '0'; // this does not work
// Only this way work
String A = Lista->Strings[0];
A[1] = '0';
Lista->Strings[0] = A;
}
I don't understand why the sign change directly in the TStringList object doesn't happen, or if it does, why isn't it written to the file.
The TStringList::Strings[] property getter DOES NOT return a reference to a String object, like you are expecting. It returns a String object by value instead, which means a copy is returned as a temporary object.
Strings[0][1] = ... has no effect because you are modifying that temporary object and not assigning it anywhere afterwards, so it just goes out of scope immediately.
That is why you need to save that temporary object to a local variable first, and then assign that variable back to the Strings[] property after modifying the variable's data.
i'm displaying a server calculated value to the enduser by using propertyChanged event.
i was using breeze 1.4.8 and i'm using the productivity stack (ms sql, web api, ef)
It was working fine.
Recently i've updated to 1.4.12 and i recognized that this event doesn't get fired anymore.
The property "A_ProvisionTotal" gets calculated serverside only.
<snip>
var token = vm.transaction.entityAspect.propertyChanged.subscribe(propertyChanged);
function propertyChanged(propertyChangedArgs) {
var propertyName = propertyChangedArgs.propertyName;
if (vm.transaction.tblEmployees.CalculationMethod == "A" && propertyName == "A_ProvisionTotal")
logSuccess('Provision neuberechnet' + '<br/>' + 'Aktuell: ' + $filter('number')(vm.transaction.Provision, 2), true);
</snip>
Let me know if this is a known regression and if you need more snippets.
A couple of thoughts for how you could accomplish your desired functionality.
The entity could remember the last calculated value in a private field. Then whenever the recalculation gets triggered, you can compare the new value to the last calculated value and if there is no change, ignore the new calculated value.
Alternatively, you could define the properties involved in your calculation as ES5 properties in the entity ctor function and then trigger the calculation in the setter of the relevant properties, when they get set with a new value. More information here: http://www.breezejs.com/documentation/extending-entities#es5-property. ES5 properties are convenient if you want to build behavior such as your calculation into setters.
Update 3
This is not a bug - see the response to this post that describes this as a documented and deliberate behavior.
Update 2 June 2014
I overlooked a key fact in your question ... one that only became clear to me after I looked at the code you included in your comments. Let me extract the key pieces for other readers:
Your test issues a query, then saves an unrelated change to the server (where the property-of-interest is updated server-side), then checks if that telltale property-of-interest raises propertyChanged when the save result is merged back into cache.
var query = EntityQuery.from("Orders").where('id', 'eq', 10248);
query.using(em).execute().then(querySucceeded).then(checkPropertyChanged).fin(start);
// querySucceed receives order 10248, updates an unrelated property (so you can save),
// wires up a propertyChanged listener and saves, returning the saveChanges promise
function checkPropertyChanged(saveResults) {
var saved = saveResults.entities[0];
// this passes because the server-side change to `Freight` was returned
ok(saved && saved.Freight() === 1200.00,
"freight got changed serverside");
// this fails because Breeze did not notify you that the `Freight` had changed
ok(notifications[0].propertyName === "Freight",
"notified serverside change of Freight Property");
}
Summarizing, you expected that a property change on the server would trigger a propertyChanged notification on the client when the entity data are re-retrieved from the server as a by-product of saveChanges.
Do I have that right?
Our documentation was not clear on whether the merge of query, save, and import entity results would raise propertyChanged.
I discussed internally and confirmed that these operations SHOULD raise propertyChanged. I also wrote another (somewhat simpler) test that reveals the bug you discovered: that merged save results may not raise propertyChanged.
We'll look into it and tell you when we've fixed it. Thanks for discovering it.
Original
We have regression tests that show that the Breeze EntityAspect.propertyChanged event is raised in v.1.4.12. For example, you can see it at work in the DocCode sample, "basicTodoTests.js"; scroll to: "Breeze propertyChanged raised when any property changes".
Can you confirm that it really is a Breeze failure? Perhaps the property you are changing is not actually an entity property? Sometimes you think you are changing an entity (e.g, your Transaction entity) but the thing whose property you changed isn't actually an entity. Then the problem is that the data you thought would be mapped to a Transaction was not ... and you can start looking for that quite different problem.
In any case, I suggest that you write a small test to confirm your suspicion ... most importantly for yourself ... and then for us. That will help us discover what is different about your scenario from our scenarios. We'll fix it if you can find it. Thanks.
Actually, I'm not sure that this is a bug. Property change events DO get fired during a save merge but the property name parameter is documented as being 'null' when fired as a result of a save.
http://www.breezejs.com/sites/all/apidocs/classes/EntityAspect.html#event_propertyChanged
From the API Docs for the 'propertyName' parameter returned by EntityAspect.propertyChanged:
The name of the property that changed. This value will be 'null' for operations that replace the entire entity. This includes queries, imports and saves that require a merge. The remaining parameters will not exist in this case either.
What may have happened between 1.4.8 and 1.4.13 is that we actually implemented our design spec more carefully and probably introduced your breaking behavior. ( which we should have documented as such but likely missed).
Update by Ward
I updated the DocCode test which first confirmed the behavior described in your question and then confirmed the documented behavior.
We do regret that we apparently neglected to implement the documented behavior earlier and that we didn't mention the breaking change in our release notes (since updated).
Here's that test:
asyncTest("propertyChanged raised when merged save result changes a property", 3, function () {
var em = newTodosEm();
var todo = em.createEntity('TodoItem', {Description: "Saved description" });
em.saveChanges().then(saveSucceeded).catch(handleFail).finally(start);
ok(todo.entityAspect.isBeingSaved, "new todo is in the act of being saved");
// This change should be overwritten with the server value when the save result is returned
// even though the entity is in an Added state and the MergeStrategy is PreserveChanges
// because save expects to merge server values into an entity it is saving
todo.Description("Changed on client before save returns");
var descriptionChanged = false;
todo.entityAspect.propertyChanged.subscribe(function (changeArgs) {
// Watch carefully! The subscription is called twice during merge
// 1) propertyName === "Id" (assigned with permanent ID)
// 2) propertyName === null (WAT?)
// and not called with propertyName === "Description" as you might have thought.
// Actually 'null' means "merged a lot of properties"
// Documented: http://www.breezejs.com/sites/all/apidocs/classes/EntityAspect.html#event_propertyChanged
// The reason for this: don't want to fire a ton of events on whole entity load
// especially when merging many entities at the same time.
if (changeArgs.propertyName === null || changeArgs.propertyName === 'Description') {
descriptionChanged = true;
}
});
function saveSucceeded(saveResult) {
var saved = saveResult.entities[0];
// passes
equal(saved && saved.Description(), "Saved description",
"the merge after save should have restored the saved description");
// fails
ok(descriptionChanged,
"should have raised propertyChanged after merge/update of 'Description' property");
}
});
I'm setting a nullable datetime field on the server but that field is not being updated in my database.
private bool BeforeSaveLeaseEntry(Lease leaseEntry, EntityInfo info)
{
if (info.EntityState == EntityState.Added)
{
leaseEntry.CreatedDate = DateTime.UtcNow.ToLocalTime();
}
if (info.EntityState == EntityState.Modified)
{
leaseEntry.LastUpdatedDate = DateTime.UtcNow.ToLocalTime();
}
return true;
}
CreatedDate is not nullable and is updated.
LastUpdatedDate is nullable and is never updated.
The code is hit but when I run a tracer on my SQL server that field is never included in the update code.
exec sp_executesql N'update [dbo].[Leases]
set [ContractNo] = #0
where ([LeaseID] = #1)
',N'#0 varchar(25),#1 int',#0='test6',#1=27415
Sorry about this, in Breeze v 1.1.3 we added a the EntityInfo.ForceUpdate boolean property but it never made it into the main Breeze documentation, it only appeared in the release notes.
This property may be used to force a server side update of an entire entity when server side modification has been made to any property of an existing entity. The other approach that may be used is to explicitly update the EntityInfo.OriginalValuesMap.
The idea behind both of these is that on an update Breeze only creates an update statement for those se properties that have been changed. Any client side changes are automatically detected because of Breeze's tracking mechanism which adds an entry into an 'originalValuesMap', but this cannot be done automatically for server side changes because the server side entities are not instrumented to perform notification about property changes.
The "EntityInfo.ForceUpdate" method forces the generation of an update statement for every property on the entity, whereas directly updating the EntityInfo.OriginalValuesMap will only update those properties found in the map.
I'm having some difficulties understanding the Concurrency problem using Update store procedures. I'm following Julie Lerman's Programming Entity Framework and she gives the following code in an example:
using (var context = new BAEntities())
{
var payment = context.Payments.First();
if (payment.PaymentDate != null)
{
payment.PaymentDate = payment.PaymentDate.Value.AddDays(1);
}
var origRowVersion = payment.RowVersion;
try
{ //BREAKPOINT #1
context.SaveChanges();
var newRowVersion = payment.RowVersion;
if (newRowVersion == origRowVersion)
{
Console.WriteLine("RowVersion not updated");
}
else
{
Console.WriteLine("RowVersion updated");
}
}
catch (OptimisticConcurrencyException)
{
Console.WriteLine("Concurrency Exception was thrown");
}
}
The Update SP looks like:
UPDATE payments
SET paymentdate=#date,reservationID=#reservationID,amount=#amount, modifieddate=#modifiedDate
WHERE
paymentid=#paymentid AND ROWVERSION=#rowversion
IF ##ROWCOUNT>0
SELECT RowVersion AS newTimeStamp FROM payments WHERE paymentid=#paymentid
and the "Use original value" checkbox is ticked in the mapping, which looks like this:
https://dl.dropboxusercontent.com/u/135754/updatemapping.png
Now, when I try to:
run the code as it is, then the newRowVersion inspected in the debugger is same as origRowversion, but the app enters 'else' clause (why is it the same in the first place, I have just changed it? is it debugger issue?)
run the code, but in the BREAKPOINT #1 I update the payment object in Management Studio, the SaveChanges throws OptimisticConcurrencyException. I assume this is expected result.
Each time when I look in the SQL Profiler, the original version of timestamp is sent to the server.
Then, when I untick the "Use original value" in the SP mappings for the timestamp value, everything works the same way as described above... I don't get the idea of it. Am I testing it wrong? When is the app supposed to enter the 'if' clause?
Thanks in advance, cheers!
EDIT:
I added newTimeStamp as the return value for the Update SP mapping. Now I can see that the updated value of RowVersion is correctly taken from the DB. But I still cannot see the difference between having "Use original value" checked and unchecked...
I think I get it now.
When I try to manually change the rowversion (to a random byte[]) before calling savechanges then:
Use Original Value unchecked: the 'random byte[]' is sent to the DB and used in the update stored procedure (in WHERE clause), causing OptimisticConcurrencyException
Use Original Value checked: the value that rowversion had when it was originally downloaded from DB is sent and used in the update stored procedure (in WHERE clause)
I guess this is what Use Original Value is for... It just seems a little weird to me, who would change it manually in the same dbcontext?
I am running into problem, where i extend the Entity to expose hasValidationError. Without that it works fine. Also i found that if I supply the ID before adding the entity it works fine as well. Why is the ID field not auto generating once the entity is extended on the client.
I am now using little different version of the code(i find it more intuitive to extend the entity this way), but it still errors out in the same way.
var Country = function () {
console.log("Country initialized");
var self = this;
self.Country_ID = ko.observable("");
self.Country_Code = ko.observable("");
self.Country_Name = ko.observable().extend({
validation: {
validator: function (val, someOtherVal) {
return false;//val === someOtherVal;
},
message: 'Invalid Value!',
params: 5
}
});
var prop = ko.observable(false);
var onChange = function () {
var hasError = self.entityAspect.getValidationErrors().length > 0;
if (prop() === hasError) {
// collection changed even though entity net error state is unchanged
prop.valueHasMutated(); // force notification
} else {
prop(hasError); // change the value and notify
}
};
// observable property is wired up; now add it to the entity
self.hasValidationErrors = prop;
//dummy property to wireup event
//should not be used for any other purpose
self.hasError = ko.computed(
{
read: function () {
self.entityAspect // ... and when errors collection changes
.validationErrorsChanged.subscribe(onChange);
},
// required because entityAspect property will not be available till Query
// return some data
deferEvaluation: true
});
self.fullName = ko.computed(
function () {
return self.Country_Code() + " --- " + self.Country_Name();
});
};
store.registerEntityTypeCtor("Country", Country);
and then in the button click i am using the following code to create new entity.
var countryType = manager.metadataStore.getEntityType("Country");
var newCountry = countryType.createEntity();
//newCountry.Country_ID(200); //if i add this line no errors occurs
newCountry.Country_Code("India");
self.list.push(newCountry);
manager.addEntity(newCountry); // validation error occurs right after this line
self.selectedItem(newCountry);
self.list.valueHasMutated();
Entities will only get their own autogenerated key if the metadata for their type specifies that this is supported. i.e.
if (myEntityType.autoGeneratedKeyType === AutoGeneratedKeyType.Identity)
This setting means that the key property of the entity is automatically generated by the server, typically for an 'Identity' column on your database.
or
if (myEntityType.autoGeneratedKeyType === AutoGeneratedKeyType.KeyGenerated)
This setting means that you have a server side KeyGenerator that can generate the key for you.
By default, however, myEntityType.autoGeneratedKeyType will equal AutoGeneratedKeyType.None.
In either of the other two cases, breeze will generate a temporary key on the client and then fix it up after a save completes with a 'real' key generated on the server.
If you do not need this capability, then simply create your own ctor for your type that generates a unique key and set it there. see MetadataStore.registerEntityTypeCtor for more details on how to register your ctor.
We are planning on improving our documentation in this area but haven't yet gotten there. I hope this helps.
Perhaps there is nothing wrong at all. How do you know that the id generation is failing? Is it a negative number after adding the newCountry to the manager? It should be.
What is the validation error that you are getting? Does it relate to Country_ID? Perhaps you have a validation constraint (e.g., minimum value) on Country_ID?
The addhasValidationErrorsProperty entity initializer works as intended. I just added a teaching test to the DocCode sample (see "Can create employee after registering addhasValidationErrorsProperty initializer" in entityExtensionTests.js). We haven't deployed it as I write this but you can get it from GitHub.
It follows your example as best I can with the Northwind Employee entity which has an identity id (Employee_ID). The test shows adding the initializer that I wrote in the previous post (not as you may have rewritten it). It shows that the new Employee's id is zero before adding to the manager and becomes -1 after adding to the manager. The -1 is the temporary id of the new employee; it receives a permanent value after save.
The default validationOptions of an EntityManager are set so to validate an entity when it is attached (or added) to the manager. You can change that behavior to suit your needs.
The new employee is in an invalid state when created; it lacks the required First and Last name values (the test displays these errors). Therefore the hasValidationErrors observable becomes true after adding the new employee to the manager and the hasValidationErrors observable raises a change notification that a Knockout UI would hear; these test shows these two points.