ThinkGeo.com    |     Documentation    |     Premium Support

InMemoryFeatureLayer.Columns.Add() is silently discarded — contradicts documented usage

Version: ThinkGeo.Core 15.0.0-beta163 (also reproduced on 15.0.0-beta001 - appears to affect the entire 15.0 beta line)

Summary

Columns added via InMemoryFeatureLayer.Columns never reach the feature source. The Add() call succeeds and throws nothing, but GetColumns() afterwards returns an empty collection. This contradicts the documented usage of the property.

Repro

InMemoryFeatureLayer layer = new();
layer.Columns.Add(new FeatureSourceColumn("geb_bez", "String", 254));

layer.Open();
var columns = layer.QueryTools.GetColumns();   // empty — expected one column
layer.Close();

Expected: GetColumns() returns the geb_bez column. Actual: returns an empty collection. The column is silently lost.

Why we expected it to work

The XML documentation on the property (present in the unobfuscated beta001 assembly) describes exactly this pattern:

Gets the attribute columns available for the features stored in the layer. Configure these columns before adding features so the InMemoryFeatureSource can store attribute values such as labels or style metadata.

And on the parameterless constructor:

Initializes a new instance of the InMemoryFeatureLayer class with no columns or features. Add columns and features after construction to populate the layer.

Apparent cause

There appear to be two separate column collections:

  • InMemoryFeatureLayer.Columns returns the protected FeatureSource.FeatureSourceColumns collection.
  • InMemoryFeatureSource.GetColumnsCore() returns a private columns field, which is only ever populated by the constructor.

Because InMemoryFeatureSource 's constructor sets CanModifyColumnStructureCore = true , FeatureSource.GetColumns() always takes its rebuild branch — it clears the list and repopulates it from GetColumnsCore() , overwriting whatever was added through Columns . So there is no configuration in which the write survives.

Workaround

Passing the columns to the constructor works, since that is the only path reaching the private field:

InMemoryFeatureLayer layer = new(
    new[] { new FeatureSourceColumn("geb_bez", "String", 254) });

FeatureSource.AddColumn() is not an alternative here — it requires an active edit transaction, so it doesn’t fit layer setup.

Suggested fix

Either route Columns mutations to the backing collection that GetColumnsCore() reads, or expose the property as a read-only view so the mistake fails at compile time rather than silently at runtime.

Hi Julian,

It’s a super good bug report! We’ve fixed it in beta167 as suggested. Columns now is that private list, so this works as documented, before or after Open():

InMemoryFeatureLayer layer = new();
layer.Columns.Add(new FeatureSourceColumn("geb_bez", "String", 254));
layer.Open();
var columns = layer.QueryTools.GetColumns();   // geb_bez

Columns added in a transaction with AddColumn land in the same list and show in Columns too.

One thing in the same corner: the layer’s own GetColumns() kept its first answer forever, so a column added after that call showed in FeatureSource.GetColumns() but not there. It now asks the source every time, and like the source it throws an exception once the layer is closed.

Thanks,
Ben

Looks good, our tests are green again!

Awesome – green is my favorite color! :grin:

1 Like