When we insert multiple rows at the same time with Entity framework it's good to add these objects one by one and then commit in the end, instead of adding and comitting each time.
So in that case, how do I get IDs for those insertions?
Example:
foreach (var item in list)
{
Subscription subscription = new Subscription();
subscription.Amount = item.ItemTotal;
this.ClientRepositories.LiveData.AddToSubscriptions(subscription);
// LiveData is db context via webservice proxy
// how to get IDS of these insertions if you do not commit each time?
int id = subscription.Id;
someOtherOperation(id); //i need to insert ID for each row that was inserted
}
this.ClientRepositories.LiveData.SaveChanges();
If I use commit inside for each time it adds new object I can easily get the latest inserted ID, but its not good to commit each time for multiple rows I heard.
I'm assuming that you are using auto incremented id's. I think your going to have to keep a list of your subscription objects outside of your loop. During each loop iteration, add the current subscription to the list. After your execute 'SaveChanges();' The subscription id's will be automatically added.
Would look like this:
List<Subscription> subList = new List<Subscription>();
foreach (var item in list)
{
Subscription subscription = new Subscription();
subscription.Amount = item.ItemTotal;
this.ClientRepositories.LiveData.AddToSubscriptions(subscription);
subList.add(subscription);
}
this.ClientRepositories.LiveData.SaveChanges();
/* At this point, each Subscription in your 'subList' should have an id */
Related
So i have written a trigger to prevent user from entering more than one opportunity product to the same opportunity, but the problem is when he adds more than one opportunity product at the same time, my trigger does not fire, salesforce takes it as one product.
What can i add to my trigger to fix this ?
My trigger :
trigger OpportunityLineItemBeforeInsert on OpportunityLineItem (before insert) {
Set<Id>opportunityIds = new Set<Id>();
// get all parent IDs
for(OpportunityLineItem i : trigger.new)
{
opportunityIds.add(i.OpportunityId);
}
// query for related Olis (Opportunity Line Items)
Map<Id, Opportunity> opps = new Map<Id, Opportunity>([SELECT ID,
(SELECT ID
FROM OpportunityLineItems)
FROM Opportunity
WHERE ID IN :opportunityIds]);
for(OpportunityLineItem i : trigger.new)
{
if(opps.get(i.OpportunityId).OpportunityLineItems.size()>0)
{
i.addError('Your Message');
}
}
}
Thank you in advance.
I would probably ignore anything related to the Oppty.
You want only one product created, so on creation, either the number of LI is 0 and you can create exactly one, or it' snot 0 and you can't create any.
I would just create a rollup field on the Oppty, count the products. If the count != 0, then fail the validation. If count = 0, then count the Olis in trigger.new and if !=1, fail.
Instead of writing code to do this you should instead create a field on products that stores the id of the parent opportunity, make that field unique, and populate the value via workflow or process builder with the id of the parent opportunity. That way if a second product gets added the unique constraint would fire and prevent the record from being inserted.
Another option would be to create a rollup on opportunity to count the number of opportunity products, then add a validation rule that show an error if the number of products > 1. The advantage of doing it this way is that you get to set the error message as opposed to the generic duplicate error message with the first option.
I am trying to create a trigger in APEX, when an custom textfield of an custom sObject gets updated with products (means, when new products get insert or existing one get deleted).
How can I compare in APEX the Trigger.Old values with the Trigger? New values of this field in order to start the Trigger.
It would look something like this:
Trigger NameOfTrigger on CustomSObject__c (after update){
/*there is already an existing list of products that get insert into the custom textfield (probably as Strings)
*/
List <String> textList = new List <String> ();
/*PseudoCode: if the textfield got updated/has changed, copy from every entry of this textfield (entry = product name as a string) and copy fieldX into another sObject
*/
if(CustomSObject.field(OldValues) != CustomSObject.field(NewValues)){
for (String product : textList){
//Trigger e.g. copy the values of a certain field of p and paste them in another sObject
}
Could somebody help me with the syntax?
You can utilize inbuilt Trigger.new and Trigger.old to get the latest and old values of any record. These lists can be used to achieve what you're looking for.
Sample example would be:
Trigger NameOfTrigger on CustomSObject__c (after update){
for(CustomSObject__c customObject : Trigger.new) {
// get old record
CustomSObject__c oldCustomObject = Trigger.oldMap.get(customObject.Id);
// compare old and new values of a particular field
if(customObject.fieldName != oldCustomObject.fieldName){
//Trigger e.g. copy the values of a certain field of p and paste them in another sObject
}
}
}
See documentation of Trigger.new & Trigger.old
I'm trying to write a trigger where inserting, updating, or deleting a record(line item) will update an amount field. Now all this records will have same ParentID (expense) and Name(of the line items). Basically duplicate records except the Contact name will be different. So, when I add a new line item with new amount with same Parents and name, the trigger should go off and query all the line items with same Parent and should re-calculate the amount.
So, if I enter first line item and say Total amount should be 100. Then I enter 2nd line Item, trigger should fire and update Amount on both record saying '50.00'. For some reason, my trigger is not updating even though it's calculating it correctly. Where is the bug? Please help!!!
trigger Test on Expense_Line_Item__c (after insert, after update, after delete) {
set<id>testlist = new set<id>();
//List<Expense_Line_Item__c> listItem= new List<Expense_Line_Item__c>();
for (Expense_Line_Item__c a : trigger.new) {
testlist.add(a.expense__c);
}
list<Expense_Line_Item__c> mapParent =
new list<Expense_Line_Item__c>([SELECT name,
id,
Amount__c
FROM Expense_Line_Item__c
WHERE expense__c IN:testlist]);
Decimal Total = 0.0;
Integer Count = 0;
for (Expense_Line_Item__c exp : mapParent) {
Total = Total + exp.Amount__c;
Count++;
System.debug('Total during iterator::::::::::::::::::::::' + Total);
System.debug('Counter:::::::::::::::::::::::::::::::::::::' + Count);
}
if (Count > = 1)
Total = Total / Count;
System.debug('Total count after division::::::::::::::::::::::' + Total);
List <Expense_Line_Item__c> insertLineItem = new List <Expense_Line_Item__c>();
for (Expense_Line_Item__c lineItem : MapParent) {
lineItem.Amount__c = Total;
//insertLineItem.add(lineItem);
//System.debug('LineItem Amount getting inserted::::::::::::::::::::::'+lineItem.Amount__c);
}
// upsert insertLineItem;
}
It looks like your code is an After trigger. As you're in after trigger context you have to do DML to update the records.
It also seems to me you're trying to do too much in a single trigger, its always better to take each method individually so that you can fully control what you're doing.
I see you do have an Upsert DML statement commented out but you'd be better off using Update for this particular instance as you're not using an external id or making a decision to create or insert which is the purpose of upsert
Been amending a piece of code to suit my needs but it won't compile. My naive eyes cannot see the error of my ways:
trigger doRollup on Time_Record__c (after insert, after update, after delete, after undelete) {
// List of parent record ids to update
Set<Id> parentIds = new Set<Id>();
// In-memory copy of parent records
Map<Id,Time_Record__c> parentRecords = new Map<Id,Time_Record__c>();
// Gather the list of ID values to query on
for(Daily_Time_Record__c c:Trigger.isDelete?Trigger.old:Trigger.new)
parentIds.add(c.Time_Record_Link__c);
// Avoid null ID values
parentIds.remove(null);
// Create in-memory copy of parents
for(Id parentId:parentIds)
parentRecords.put(parentId,new Time_Record__c(Id=parentId,RollupTarget__c=0));
// Query all children for all parents, update Rollup Field value
for(Daily_Time_Record__c c:[select id,FieldToRoll__c,Time_Record_Link__c from Daily_Time_Record__c where id in :parentIds])
parentRecords.get(c.Time_Record_Link__c).RollupTarget__c += c.FieldToRoll__c;
// Commit changes to the database
Database.update(parentRecords.values());
}
Where:
Time_Record__c is my parent
RollUpTarget__c is the place I am trying to roll to
Daily_Time_Record__c is the child
Time_Record_Link__c is the link to parent that exists on the child
FieldToRoll__c is a test field to roll up on the child
I think that is what I had to replace from this generic template:
trigger doRollup on Child__c (after insert, after update, after delete, after undelete) {
// List of parent record ids to update
Set<Id> parentIds = new Set<Id>();
// In-memory copy of parent records
Map<Id,Parent__c> parentRecords = new Map<Id,Parent__c>();
// Gather the list of ID values to query on
for(Child__c c:Trigger.isDelete?Trigger.old:Trigger.new)
parentIds.add(c.ParentField__c);
// Avoid null ID values
parentIds.remove(null);
// Create in-memory copy of parents
for(Id parentId:parentIds)
parentRecords.put(parentId,new Parent__c(Id=parentId,RollupField__c=0));
// Query all children for all parents, update Rollup Field value
for(Child__c c:[select id,Amount__c,ParentField__c from Child__c where id in :parentIds])
parentRecords.get(c.ParentField__c).RollupField__c += c.Amount__c;
// Commit changes to the database
Database.update(parentRecords.values());
}
Ideally I want to roll up a sum of 7 fields from the child (hence needing this solution), but starting small.
Using LINQ-to-Entities 4.0, is there a correct pattern or construct for safely implementing "if not exists then insert"?
For example, I currently have a table that tracks "user favorites" - users can add or remove articles from their list of favorites.
The underlying table is not a true many-to-many relationship, but instead tracks some additional information such as the date the favorite was added.
CREATE TABLE UserFavorite
(
FavoriteId int not null identity(1,1) primary key,
UserId int not null,
ArticleId int not null
);
CREATE UNIQUE INDEX IX_UserFavorite_1 ON UserFavorite (UserId, ArticleId);
Inserting two favorites with the same User/Article pair results in a duplicate key error, as desired.
I've currently implemented the "if not exists then insert" logic in the data layer using C#:
if (!entities.FavoriteArticles.Any(
f => f.UserId == userId &&
f.ArticleId == articleId))
{
FavoriteArticle favorite = new FavoriteArticle();
favorite.UserId = userId;
favorite.ArticleId = articleId;
favorite.DateAdded = DateTime.Now;
Entities.AddToFavoriteArticles(favorite);
Entities.SaveChanges();
}
The problem with this implementation is that it's susceptible to race conditions. For example, if a user double-clicks the "add to favorites" link two requests could be sent to the server. The first request succeeds, while the second request (the one the user sees) fails with an UpdateException wrapping a SqlException for the duplicate key error.
With T-SQL stored procedures I can use transactions with lock hints to ensure a race condition never occurs. Is there a clean method for avoiding the race condition in Entity Framework without resorting to stored procedures or blindly swallowing exceptions?
You can also write a stored procedure that uses some new tricks from sql 2005+
Use your combined unique ID (userID + articleID) in an update statement, then use the ##RowCount function to see if the row count > 0 if it's 1 (or more), the update has found a row matching your userID and ArticleID, if it's 0, then you're all clear to insert.
e.g.
Update tablex set userID = #UserID, ArticleID = #ArticleID (you could have more properties here, as long as the where holds a combined unique ID) where userID = #UserID and ArticleID = #ArticleID
if (##RowCount = 0)
Begin
Insert Into tablex ...
End
Best of all, it's all done in one call, so you don't have to first compare the data and then determine if you should insert. And of course it will stop any dulplicate inserts and won't throw any errors (gracefully?)
You could try to wrap it in a transaction combined with the 'famous' try/catch pattern:
using (var scope = new TransactionScope())
try
{
//...do your thing...
scope.Complete();
}
catch (UpdateException ex)
{
// here the second request ends up...
}