I have a layer with over 250,000+ features, and I want to update all the attribute data using ArcObjects and C#. I have written the code below and it takes around 3 hours to perform the update. Can you suggest whether the code below is efficient enough and any suggestion regarding the delta tables please?
public void updateWithCursor(IWorkspace workspace, IFeatureClass featureClass, List<Update> updateList)
{
using (ComReleaser comReleaser = new ComReleaser())
{
IWorkspaceEdit workspaceEdit = (IWorkspaceEdit)workspace;
// Start an edit session
workspaceEdit.StartEditing(false);
// Start an edit operation
workspaceEdit.StartEditOperation();
// Use IFeatureClass.Search to create a search cursor.
IFeatureCursor searchCursor = featureClass.Search(null, false);
comReleaser.ManageLifetime(searchCursor);
// Find the positions of the fields used to get and set values.
int idIndex = featureClass.FindField("ID");
int serialIndex = featureClass.FindField("SERIAL");
int cityIndex = featureClass.FindField("CITY");
int updateDateIndex = featureClass.FindField("UPDATEDATE");
int counter = 0;
IFeature feature = null;
while ((feature = searchCursor.NextFeature()) != null)
{
string searchValue = feature.get_Value(idIndex).ToString();
if (searchValue != "")
{
Update upd = updateList.SingleOrDefault(x => x.id == searchValue);
if (upd != null)
{
feature.set_Value(serialIndex , upd.serialNo);
feature.set_Value(cityIndex , upd.city);
feature.set_Value(updateDateIndex, DateTime.Now);
feature.Store();
}
Console.WriteLine(counter++);
}
}
workspaceEdit.StopEditOperation();
// Stop the edit session
workspaceEdit.StopEditing(true);
}
}
-
You should use a Dictionary instead of a List for your updates, see my answer below.crmackey– crmackey2018年02月17日 15:55:48 +00:00Commented Feb 17, 2018 at 15:55
2 Answers 2
The process you are doing should not take that long, and the bottleneck is your UpdateList
. On each iteration of of your search cursor, the UpdateList
is searched from the beginning until a matching ID
is found. It would be much more efficient to use a HashTable or a Dictionary so you can create a lookup via the ID
to update your table. From this page:
Both lists and dictionaries are used to store collections of data. The Dictionary is based on a hash table, that means it uses a hash lookup, which is a rather efficient algorithm to look up things, on the other hand, a list you have to go element by element until it finds the result from beginning to the result each time.
So what you can do is set up your UpdateList
as a Dictionary where your values are a Dictionary where every entry would be:
UpdateDict[id] = valueDict //where valueDict is {"city": "Chicago", "serialNo": 100}
My C# is pretty rusty, but maybe in your search cursor you can do something like this:
while ((feature = searchCursor.NextFeature()) != null)
{
string searchValue = feature.get_Value(idIndex).ToString();
if ((searchValue != "") && (UpdateDict.ContainsKey(searchValue)))
{
Dictionary upd = UpdateDict[searchValue]; //use this update dict instead of list
if (upd != null)
{
feature.set_Value(serialIndex , upd["serialNo"]);
feature.set_Value(cityIndex , upd["city"]);
feature.set_Value(updateDateIndex, DateTime.Now);
feature.Store();
}
Console.WriteLine(counter++);
}
}
I would ditch the search cursor and do something like this:
foreach(var upd in updateList)
{
IFeature feature = featureClass.GetFeature(upd.id);
feature.set_Value(serialIndex , upd.serialNo);
feature.set_Value(cityIndex , upd.city);
feature.set_Value(updateDateIndex, DateTime.Now);
feature.Store();
}
Now you don't have to search through updateList or put it in a hashtable.
Also, if this is a standalone program (not an Add-in) then you'll want to wrap all your ArcObjects code to run in not only the same thread, but a SingleThreadedApartment thread.
So something like this would do:
static void Main(string[] args)
{
Thread t = new Thread((x) =>
{ //ArcObjects code
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
The reason is that all ArcObjects are SingleThreadedApartment [STA] COM objects. When using an STA COM object created on a different thread there is a significant delay when marshaling calls between threads. STA objects created in a MultiThreadedApartment [MTA] (which is the default for Console apps, WPF, Forms, etc.) will actually be created on a separate STA thread. This means any interaction with ArcObjects will incur the performance penalty for marshaling calls between threads. By creating an STA thread that penalty is removed.