Below is my code which am calling and am getting below exception.
Collection was modified; enumeration operation might not execute.
In this code am checking if dataset contains tb_error table name and then checking the row count.
If rowcount> 1 , insert into db.
after that i want to clear that table and after that i need to clear other view also.
Please help me where to modify my code.
if (MainClass.OutputDataset.Tables.Contains(tb_error.TableName))
{
foreach (DataRow drErr in MainClass.OutputDataset.Tables[tb_error.TableName].Rows)
{
//insert into DB
}
}
if (MainClass.OutputDataset != null && MainClass.OutputDataset.Tables["tb_error"].Rows.Count > 0)
{
MainClass.OutputDataset.Tables["tb_error"].Clear();
}
MainClass.dsinput.Tables.Remove("BSData_VW");
}
This happens because the underlying collection has since had items added or removed, which invalidates the loop.
You can get around this by taking a snapshot, e.g.:
foreach (DataRow drErr in MainClass.OutputDataset.Tables[tb_error.TableName].Rows.ToList())
{
//insert into DB
}
The key is the .ToList() call at the end, which means the foreach loop only operates on Rows as it is at the point-in-time.
When you get an error like that, you pretty much have to abandon using foreach and come up with some other looping mechanism. You can try using a for statement or rolling your own with a variable and a while statement.
Related
How to call Page.ClientScript.RegisterStartupScript from foreach for each iteration. It looks like it call all iterations once at end
int saveImageCount=0;
foreach (DataRow Stdrow in key.ColumnValues)
{
saveImageCount++
Page.ClientScript.RegisterStartupScript(GetType(), "SaveImage" + saveImageCount, "javascript:SaveImage();", true);
}
Unable to call javascript:SaveImage for each iteration
They will all have the same type, so they'll override each other until the last one, and the last one will "win".
Instead build up a string and then, outside of the foreach, register the string.
#Jonathan,I think you are a little bit mislead,its Type and key that makes unique combination not only type.
So #brijesh is 95% right but he has to use Register.ClientScriptBlock as it will be registered based on event not on page load.
int saveImageCount = 0;
foreach (DataRow Stdrow in key.ColumnValues)
{
saveImageCount++;
ClientScript.RegisterClientScriptBlock(this.GetType(), "script"+saveImageCount, "alert('data has been added successfully');", true);
}
GetType and "script"+saveImageCount will make a unique key combination and hence your script will be called without hesitation.
for (int i=0; i<mycolumns.length; i++)
{
where.and(QueryBuilder.eq(COLNAME, mycolumns[i]));
//how to remove the above and() call
}
In every iteration of the loop, I want to execute the query and then substitute the value in next loop iteration.
I'm not completely clear on what you are trying to accomplish. I am guessing that you are trying to update multiple rows sharing a primary key, updating 1 row at a time?
Unfortunately this isn't possible since when you call where.and you are adding data to the Where object and it is returning you a reference to the same Where object.
In short, Where is not immutable and neither is the Statement it belongs to, so you won't get a new copy every time you call it, rather you get an updated version of the Where object.
What you could do is generate your Statement again (whether it be QueryBuilder.update,delete, or insert) in the loop like:
for (int i=0; i<mycolumns.length; i++) {
Statement stmt = QueryBuilder.update("tableName").where(eq("key", 1)).and(QueryBuilder.eq(COLNAME, mycolumns[i]));
session.execute(stmt);
}
I have records that have an index attribute to maintain their position in relation to each other.
I have a plugin that performs a renumbering operation on these records when the index is changed or new one created. There are specific rules that apply to items that are at the first and last position in the list.
If a new (or existing changed) item is inserted into the middle (not technically the middle...just somewhere between start and end) of the list a renumbering kicks off to make room for the record.
This renumbering process fires in a new execution pipeline...We are updating record D. When I tell record E to change (to make room for D) that of course fires the plugin on update message.
This renumbering is fine until we reach the end of the list where the plugin then gets into a loop with the first business rule that maintains the first and last record differently.
So I am trying to think of ways to pass a flag to the execution context spawned by the renumbering process so the recursion skips the boundary edge business rules if IsRenumbering == true.
My thoughts / ideas:
I have thought of using the Depth check > 1 but that isn't a reliable value as I can't explicitly turn it on or off....it may happen to work but that is not engineering a solid solution that is hoping nothing goes bump. Further a colleague far more knowledgeable than I said that when a workflow calls a plugin the depth value is off and can't be trusted.
All my variables are scoped at the execute level so as to avoid variable pollution at the class level....However if I had a dictionary object, tuple, something at the class level and one value would be the thread id and the other the flag value then perhaps my subsequent execution context could check if the same owning thread id had any values entered.
Any thoughts or other ideas on how to pass context information to a new pipeline would be greatly appreciated.
Per Nicknow sugestion I tried sharedvariables but they seem to be going out of scope...:
First time firing post op:
if (base.Stage == EXrmPluginStepStage.PostOperation)
{
...snip...
foreach (var item in RenumberSet)
{
Context.ParentContext.SharedVariables[recordrenumbering] = "googly";
Entity renumrec = new Entity("abcd") { Id = item.Id };
#region We either add or subtract indexes based upon sortdir
...snip...
renumrec["abc_indexfield"] = TmpIdx + 1;
break;
.....snip.....
#endregion
OrganizationService.Update(renumrec);
}
}
Now we come into Pre-Op of the recursion process kicked off by the above post-op OrganizationService.Update(renumrec); and it seems based upon this check the sharedvariable didn't carry over...???
if (!Context.SharedVariables.Contains(recordrenumbering))
{
//Trace.Trace("Null Set");
//Context.SharedVariables[recordrenumbering] = IsRenumbering;
Context.SharedVariables[recordrenumbering] = "Null Set";
}
throw invalidpluginexception reveals:
Sanity Checks:
Depth : 2
Entity: ...
Message: Update
Stage: PreOperation [20]
User: 065507fe-86df-e311-95fe-00155d050605
Initiating User: 065507fe-86df-e311-95fe-00155d050605
ContextEntityName: ....
ContextParentEntityName: ....
....
IsRenumbering: Null Set
What are you looking for is IExecutionContext.SharedVariables. Whatever you add here is available throughout the entire transaction. Since you'll have child pipelines you'll want to look at the ParentContext for the value. This can all get a little tricky, so be sure to do a lot of testing - I've run into many issues with SharedVariables and looping operations in Dynamics CRM.
Here is some sample (very untested) code to get you started.
public static bool GetIsRenumbering(IPluginExecutionContext pluginContext)
{
var keyName = "IsRenumbering";
var ctx = pluginContext;
while (ctx != null)
{
if (ctx.SharedVariables.Contains(keyName))
{
return (bool)ctx.SharedVariables[keyName];
}
else ctx = ctx.ParentContext;
}
return false;
}
public static void SetIsRenumbering(IPluginExecutionContext pluginContext)
{
var keyName = "IsRenumbering";
var ctx = pluginContext;
ctx.SharedVariables.Add(keyName, true);
}
A very simple solution: add a bit field to the entity called "DisableIndexRecalculation." When your first plugin runs, make sure to set that field to true for all of your updates. In the same plugin, check to see if "DisableIndexRecalculation" is set to true: if so, set it to null (by removing it from the TargetEntity entirely) and stop executing the plugin. If it is null, do your index recalculation.
Because you are immediately removing the field from the TargetEntity if it is true the value will never be persisted to the database so there will be no performance penalty.
I'm relatively new to Apex, but I have some questions about a batch job that I am creating. I want to make a query with a subquery (please see the code). Every Portal_c can have more than 200 Exporte_r.
global Database.QueryLocator start(Database.BatchableContext BC) {
String query = 'SELECT Id, Name, (SELECT Id FROM Exporte__r) FROM Portal__c';
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<Portal__c> scope) {
for (Portal__c portal : scope) {
// doesn't work -> First error: Aggregate query has too many rows for direct assignment, use FOR loop
// when using FOR loop -> System.QueryException: invalid query locator
//List<Export__c> relatedExports = portal.Exporte__r;
// grab all the related Export__c records using 'getSObjects' to avoid errors described above
Export__c[] relatedExports = portal.getSObjects('Exporte__r');
if (relatedExports != null) {
for (Export__c exp : relatedExports) {
// do something
}
}
}
}
I have the following questions:
If I use List<Export__c> relatedExports = portal.Exporte__r (which I commented out) to get the sub query records then I will receive the error message: “Aggregate query has too many rows for direct assignment, use FOR loop”. The error message makes no sense for me as the SOQL is done already before. Is there any explaination?
With the solution above the maximal amount of records from type Exporte_r received per Portal_c with the sub query is 199 though I have more than 200 for some records of Portal__c, why is it limited to that number? It seems all records above 199 are ignored in this case.
Is there any possibility to receive more than 199 records from a sub query? I have tried to change the batch size but it seems it is independent of the number of records receivable by the sub query. Any idea?
Many thanks!
As per the salesforce doc http://www.salesforce.com/us/developer/docs/apexcode/Content/langCon_apex_loops_for_SOQL.htm
You might get a QueryException in a SOQLfor loop with the message
Aggregate query has too many rows for direct assignment, use FOR loop.
This exception is sometimes thrown when accessing a large set of child
records of a retrieved sObject inside the loop, or when getting the
size of such a record set. To avoid getting this exception, use a for
loop to iterate over the child records, as follows.
Integer count=0;
for (Contact c : returnedAccount.Contacts) {
count++;
// Do some other processing
}
I'm writing an event handler to handle the updating of a particular SPItem in a list. The event is asynchronous and I get the SPEvenItemProperties without a problem. What I would like to find out is which of the SPItems columns' actually fired the event. Anyone have any idea how?
Thanks in advance.
Your answer depends a bit on from where and how the SPListItem is retrieved. In a regular list, you do not have access to the previous values of the item. If you turn on versioning, you can get access to the previous versions, depending on permissions, of course.
For a document library you can use the SPItemEventProperties.BeforeProperties to get the previous metadata for a document.
For a document library you can try something like this:
foreach (DictionaryEntry key in properties.BeforeProperties)
{
string beforeKey = (string)key.Key;
string beforeValue = key.Value.ToString();
string afterValue = "";
if (properties.AfterProperties[beforeKey] != null)
{
afterValue = properties.AfterProperties[beforeKey].ToString();
if (afterValue != beforeValue)
{
// Changed...
}
}
}
.b
I think the best way to do this would be to look through the BeforeProperties and AfterProperties of the SPItemEventProperties and check which fields have different values.
These contain the values of all fields of the item before and after the event occurred.