Does LINQ to SQL support Single Unit of work only or can I persist the Context and do Next operation? - linq-to-sql

I have a windows application. I am trying to insert a record through a DataContext. It has Unique identifier in the table. Even I am executing a trigger after insertion. So I am making a select query in the end of the trigger to get the auto generator number and to avoid auto-sync error. As it's a windows application I can keep the Context for longtime. When I create a new object ( for example order) and do the same previous operation, upon SubmitChanges operation, it shows cannot have duplicate key. Why can't I use this same Context to Insert the second record? Or do I need to create a new Context to insert a new Record?(Does this Unit of work Concept comes here?). Creating new Context is bad idea as I need to load all data again..
Any thought?
Some code sample to explain my situation:
CallCenterLogObjCotext = (CallCenterLogObjCotext == null ? (new CallcenterLogContext) : (CallCenterLogObjCotext));
CallDetail newCallDetailsOpenTicket = new CallDetail();
newCallDetailsOpenTicket.CallPurpose = (from callpuposelist in CallCenterLogObjCotext.CallPurposes
where callpuposelist.CallPurposeID == ((CallPurpose)(cbcallpurpose.SelectedItem)).CallPurposeID
select callpuposelist).FirstOrDefault();
Lots of settings like this ...
CallCenterLogObjCotext.CallDetails.InsertOnSubmit(newCallDetailsOpenTicket);
CallCenterLogObjCotext.SubmitChanges();
As I mentioned above, this is a click on Open Ticket button on windows form. I change the values of fname, lname and all in the textboxes available on that form and clicked the same button.
So it will call the same method again. I get the below specified error:
System.Data.Linq.DuplicateKeyException: Cannot add an entity with a key that is already in use.

You can insert more than one row with the same context object, see http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx, http://msdn.microsoft.com/en-us/library/bb425822.aspx, and other numerous online examples. The duplicate key issue could be a linq to sql configuration issue, or a database integrity error, i.e. such as if you have a natural primary key on a table and are trying to insert a row with the same natural primary key more than once.

Related

trigger update copy information

I want to create a trigger that does the following:
copy one column of info jobnumber from one table (jobs) to another (materials) on an existing record to attachedjobnumber.
I haven't found the correct syntax to say this. when I insert a new job - nothing gets update and no new row is inserted,,, but there are no error messages in the logs.
I also need to set the bool (hasjobnumber) equal to true - when I test that trigger - it works fine.
which makes me think that setting the value of material.attachedjobnumber = jobs.jobnumber is the problem, my guess is that jobs.jobnumber isn't in reference when updating table material...
if that's true - what's the proper syntax for this?
I've tested separate triggers, and so far this trigger works fine.
UPDATE material
SET isjobyet = "HAS"
WHERE barcode1 IN (
SELECT primaryRFID
FROM jobs
WHERE jobs.primaryRFID = material.barcode1
)
since this code does work - I make the assumption that the non-static JobNumber value is the source of the problem. since "HAS" is correctly updated.
UPDATE material
SET material.AttachedJobNumber = jobs.JobNumber
WHERE barcode1 IN (
SELECT primaryRFID
FROM jobs
WHERE jobs.primaryRFID = material.barcode1
)
from this - I expect that after each inserts on the table jobs:
jobs.JobNumber be assigned to the material.AttachedJobName
this updates only the material row where the material.barcode1 =jobs.primaryrfid.
but no new row is inserted at all.
Before you perform UPDATE,
Actually you can use the same script using SELECT [skip the UPDATE SYNTAX]
That way you can monitor your script without committing anything yet.
And also I dont recommend using this inside the subquery
WHERE jobs.primaryRFID = material.barcode1
This condition connecting a table works on IN-SELECT subquery.
If you are performing subqueries inside the [WHERE] clause, try to treat it as different buffer, dont connect them first.

How to update multiple records in same table using .AfterUpdate data macro without error "A data macro resource limit was hit."

I have a table tblItems with a list of inventory items. The table has many columns to describe these items, including columns for SupplierName, SupplierOrderNumber and PredictedArrivalDate.
If I order several new items from a supplier, I will record each item separately in the table with the same supplier name, order number and a predicted arrival date.
I would like to add a data macro, so that if I update the PredictedArrivalDate for one record, the value will be copied to the PredictedArrivalDate column of other records/items with the same SupplierName AND SupplierOrderNumber.
The closest I've got is:
SetLocalVar (MySupplierName, [SupplierName])
SetLocalVar (MySupplierOrderNumber , [SupplierOrderNumber ])
SetLocalVar (MyPredictedArrivalDate, [PredictedArrivalDate])
For Each Record in tblItems
Where Condition = [SupplierOrderNumber] Like [MySupplierOrderNumber] And [SupplierName] Like [MySupplierName] And [PredictedArrivalDate]<>[MyPredictedArrivalDate]
Alias OtherRecords
EditRecord
SetField ([OtherRecords].[PredictedArrivalDate], [MyPredictedArrivalDate])
End EditRecord
However, when I run this, only 5 records update, and the error log reports error -20341:
"A data macro resource limit was hit. This may be caused by a data
macro recursively calling itself. The Updated() function may be
used to detect which field in a record has been updated to help
prevent recursive calls."
How can I get this working?
I'm not one for using macro's to do anything, so I'd use VBA and recordsets/an action query to do the updating.
You can call a user-defined function inside a data macro by setting a local var equal to its result.
Access doesn't like data macros triggering themselves (which you are doing, you're using an on update macro and updating fields in the same table on a different record), because there is a risk of accidentally creating endless loops. Looks like you triggered a measure that's made to prevent this. I'd try to avoid that as much as possible.
Note: using user-defined functions inside data macros can cause problems when you're linking to the table from outside of Access (via ODBC for example).
This isn't a good solution (it's not a data macro), but it does work as a temporary fix.
I created an update query called "updatePredictedArrivalDate":
PARAMETERS
ItemID Long,
MyPredictedArrivalDate DateTime,
MySupplierName Text ( 255 ),
MySupplierOrderNumber Text ( 255 );
UPDATE tblItems
SET tblItems.PredictedArrivalDate = [MyPredictedArrivalDate]
WHERE (((tblItems.SupplierName) = [MySupplierName])
AND ((tblItems.SupplierOrderNumber) = [MySupplierOrderNumber])
AND ((tblItems.ID) <> [ItemID]));
On the PredictedArrivalDate form field .AfterUpdate event, I then added this macro:
IF [PredictedArrivalDate].[OldValue]<>[PredictedArrivalDate] Or [PredictedArrivalDate]<>""
OpenQuery (updatePredictedArrivalDate, Datasheet, Edit, [ID], [PredictedArrivalDate], [SupplierName], [SupplierOrderNumber])
I now have to remember to add this .AfterUpdate event to any other forms I create that amend that particular field.
If anyone has a better solution, please let me know.

How do i append an auto increment primary key to another field in the same table?

I'm using yii active records for mysql, and i have a table where there's a field that needs to be appended with the primary key of the same table. The primary key is an auto increment field, hence i can't access the primary key before saving.
$model->append_field = "xyz".$model->id; // nothing is appending
$model->save();
$model->append_field = "xyz".$model->id; //id is now available
How do i do this?
I know that i can update right after insertion, but is there a better method?
Your record is only assigned an id after the INSERT statement is executed. There is no way to determine what that id is prior to INSERT, so you would have to execute an UPDATE with the concatenated field value after your INSERT.
You could write a stored procedure or trigger in MySQL to do this for you, so your app executes a single SQL statement to accomplish this. However, you are just moving the logic into MySQL and in the end both an INSERT and UPDATE are occurring.
Some more workarounds:
This is almost your approach ;)
$model->save();
$model->append_field = "xyz".$model->id; //id is now available
$model->save();
But you could move this functionality to a behavior with a custom afterSave() method, note that you'd have to take care about not looping the event.
Or just write a getter for it
function getFull_append_field(){
return $this->append_field.$this->id;
}
but then you can not use it in a SQL statement, unless you create the attribute there with CONCAT() or something similar.
Anyone else coming to this question might be interested in exactly how i implemented it, so here's the code :
//in the model class
class SomeModel extends CActiveRecord{
...
protected function afterSave(){
parent::afterSave();
if($this->getIsNewRecord()){
$this->append_field=$this->append_field.$this->id;
$this->updateByPk($this->id, array('append_field'=>$this->append_field));
}
}
}
One way to avoid the looping the event(as mentioned by #schmunk) was to use saveAttributes(...) inside the afterSave() method, but saveAttributes(...) checks isNewRecord, and inserts a value only if it is a new record, so that requires us to use setNewRecord(false); before calling saveAttributes(...).
I found that saveAttributes(...) actually calls updateByPk(...) so i directly used updateByPk(...) itself.

Define custom POST method for MyDAC

I have three tables objects, (primary key object_ID) flags (primary key flag_ID) and object_flags (cross-tabel between objects and flags with some extra info).
I have a query returning all flags, and a one or zero if a given object has a certain flag:
SELECT
f.*,
of.*,
of.objectID IS NOT NULL AS object_has_flag,
FROM
flags f
LEFT JOIN object_flags of
ON (f.flag_ID = of.flag_ID) AND (of.object_ID = :objectID);
In the application (which is written in Delphi), all rows are loaded in a component. The user can assign flags by clicking check boxes in a table, modifying the data.
Suppose one line is edited. Depending on the value of object_has_flag, the following things have to be done:
If object_has_flag was true and still is true, an UPDATE should be done on the relevant row in objects_flags.
If object_has_flag was false but is now true, and INSERT should be done
If object_has_flag was true, but is now false, the row should be deleted
It seems that this cannot be done in one query https://stackoverflow.com/questions/7927114/conditional-replace-or-delete-in-one-query.
I'm using MyDAC's TMyQuery as a dataset. I have written separate code that executes the necessary queries to save changes to a row, but how do I couple this to the dataset? What event handler should I use, and how do I tell the TMyQuery that it should refresh instead of post?
EDIT: apparently, it is not completely clear what the problem is. The standard UpdateSQL, DeleteSQL and InsertSQL cannot be used because sometimes after editing a line (not deleting it or inserting a line), an INSERT or DELETE has to be done.
The short answer is, to paraphrase your answer here:
Look up the documentation for "Updating Data with MyDAC Dataset Components" (as of MyDAC 5.80).
Every TCustomDADataSet (such as TMyQuery) descendant has the capability to set update SQL statements using SQLInsert, SQLUpdate and SQLDelete properties.
TMyUpdateSQL is also a promising component for custom update operations.
It seems that the easiest way is to use the BeforePost event, and determine what has to be done using the OldValue and NewValue properties of several fields.

INSERT and UPDATE the same row in the same TRANSACTION? (MySQL)

So here's my problem:
I have an article submission form with an optional image upload field.
When the user submits the form - this is roughly what happens:
if($this->view->form->isValid($_POST){
$db->beginTransaction();
try{
// save content of POST to Article table
if(!$this->_saveArticle($_POST)){
return;
}
// resize and save image using ID generated by previous condition
if(!$this->_saveImage($_FILES){
$db->rollback();
return;
}
// update record if image successfully generated
if(!$this->_updateArticle(){
$db->rollback();
}
$db->commit();
}
}catch (Exception $e){
$db->rollback()
}
All Models are saved using mappers, which automate "UPSERT" functionality by checking for the existence of a surrogate key
public function save($Model){
if(!is_null($Model->id_article){
$Mapper->insert($Model->getFields());
return;
}
$Mapper->update($Model->getFields(),$Model->getIdentity());
}
The article table has a composite UNIQUE index of ID,Title and URL. In addition, I'm generating a UID that gets added to the ID field of the Model prior to insert (instead of auto-incrementing)
When I try to execute this, it runs fine for the first article inserted into the table - but subsequent calls (with radically different input) triggers a DUPLICATE KEY error. MySQL throws back the ID generated in condition 1 (_saveArticle) and complains that the key already exists...
I've dumped out the Model fields (and the condition state - i.e. insert | update) and they proceed as expected (pseudo):
inserting!
id = null
title = something
content = something
image = null
updating!
id = 1234123412341234
title = something
content = something else
image = 1234123412341234.jpg
This row data is not present in the database.
I figure this could be one of a few things:
1: I'm loading a secondary DB adapter on user login, allowing them to interface with several sites from one login - this might be confusing the transaction somehow
2: It's a bug of some description in the Zend transaction implementation (possibly triggered by 1)
3: I need to replace the save() with an INSERT ... ON DUPLICATE
4: I should restructure the submission process, or generate a name for the image that isn't dependent on the UID of the previously inserted row.
Still hunting, but I was wondering if anyone else has encountered this kind of issue or could point me in the direction of a solution
best SWK
OK - just for the record, this is entirely possible. The problem was in my application architecture. I was catching Exceptions in my Mapper classes that were handling persistence - and then querying them to return boolean states and thus interrupt the process. This was in turn breaking the try/catch loop which was preventing the insert/update from working correctly.
To summarise - Yes - you CAN insert and update the same row in a single transaction. I've ticked community wiki to cancel rep out