[CONNECT C#.NET] ECQuery WhereClause for ECProperties in nested ECClasses help

Even though I am in a 2D drawing, we are using ProStructures v10.7.2.33 (in MicroStation mode) as our base platform with Connect SDK v10.16.02.022 on Windows 11 Enterprise

I have an ECSchema that contains an ECClass which contains ECProperties and other nested (IsStruct) ECClasses as ECProperties.
I have no trouble using either PropertyExpression or QueryHelper.WherePropertyExpressions to add a WhereClause to my ECQuery for getting instances from the dgn that match the WhereCriteria for "non-nested" properties.
I can NOT figure out how to do this for properties that belong to the nested ECClasses. I have tried many ways with no success. Is this a bug, or maybe something that was never implemented?
I know I can get all instances and then use Linq to post-process. But, if possible, I would prefer to get only the instances that match the criteria from the dgn to start with.
If there is a way to do it please help me?

I do have a static test function, that takes an ElementId param, and...
1. Creates and imports a [simple] schema that has one non-nested property and two nested classes.
2. Attaches an instance to the (existing) Element whose Id was passed in.
3. Instantiates a FindInstancesScope, to find instances on DgnECHostType.Element, and a query with a WhereClause to find by the non-nested property - this works fine.
4. Re-Instantiates the query with a WhereClase to find by a nested property - this does NOT work?

If you need it I can add it.

Parents
  • I also attached the file that contains the standalone function that can be plugged in somewhere and tested, all you need is an ElementId.

    The test schema  looks like this when serialized to xml:

    <?xml version="1.0" encoding="utf-16"?>
    <ECSchema schemaName="Gadget" nameSpacePrefix="aSa.Studio" version="1.0" xmlns="">www.bentley.com/.../Bentley.ECXML.2.0">
    <ECClass typeName="ValuesClass" isStruct="True" isDomainClass="True">
    <ECProperty propertyName="Tag" typeName="string" displayLabel="Gadget Tag" readOnly="True" />
    <ECProperty propertyName="Inventor" typeName="string" readOnly="True" />
    </ECClass>
    <ECClass typeName="KeysClass" isStruct="True" isDomainClass="True">
    <ECProperty propertyName="GrpNum" typeName="int" readOnly="True" />
    </ECClass>
    <ECClass typeName="GadgetStuff" displayLabel="Gadget Stuff" isDomainClass="True">
    <ECStructProperty propertyName="Values" typeName="ValuesClass" />
    <ECStructProperty propertyName="Keys" typeName="KeysClass" />
    <ECProperty propertyName="Version" typeName="string" readOnly="True" />
    </ECClass>
    </ECSchema>

    Here I attempt (SUCCESSFULLY) to find the instance by the Version number (non-nested property)

    var dataClass = schema.GetClass("GadgetStuff");

    FindInstancesScope scope = FindInstancesScope.CreateScope(Session.Instance.GetActiveDgnFile(), new FindInstancesScopeOption(DgnECHostType.Element, false));

    var query = new ECQuery(dataClass);
    PropertyExpression exp = new PropertyExpression(RelationalOperator.EQ, dataClass["Version"], "1.0");
    query.WhereClause.Add(exp);
    query.SelectClause.SelectAllProperties = true;

    var dgnInstances = DgnECManager.Manager.FindInstances(scope, query); // this works fine

    Here I attempt (WITHOUT SUCCESS) to find the instance by the nested "Inventor" property:

    query = new ECQuery(dataClass);
    foreach (var p in schema.GetProperties())
    {
    if (p.Name == "Inventor")
    {
    prop = p as ECProperty;
    break;
    }
    }
    exp = new PropertyExpression(RelationalOperator.EQ, prop, "aSa"); // I have tried many other ways to set the WhereCLause too, none worked.
    query.WhereClause.Add(exp);
    query.SelectClause.SelectAllProperties = true;

    dgnInstances = DgnECManager.Manager.FindInstances(scope, query); // does not work -> just doesn't find anything

    		public static bool GadgetTest(ElementId existingElmId)
    		{
    			int verMajor = 1, verMinor = 0;
    			ECSchema schema = new ECSchema("Gadget", verMajor, verMinor, "aSa.Studio");
    
    			ECClass dataClass = new ECClass("GadgetStuff"); // Main Class CANNOT be "IsStruct" type.
    			dataClass.DisplayLabel = "Gadget Stuff";
    
    			ECClass valClass = new ECClass("ValuesClass", true); // IsStruct
    			var prop = new ECProperty("Tag", ECObjects.StringType);
    			prop.DisplayLabel = "Gadget Tag";
    			prop.IsReadOnly = true;
    			valClass.Add(prop);
    			prop = new ECProperty("Inventor", ECObjects.StringType);
    			prop.DisplayLabel = "Inventor";
    			prop.IsReadOnly = true;
    			valClass.Add(prop);
    			//schema.AddClass(valClass); // not necessary - gets added with dataClass
    
    			prop = new ECProperty("Values", valClass);
    			dataClass.Add(prop);
    
    			ECClass keysClass = new ECClass("KeysClass", true); // IsStruct
    			prop = new ECProperty("GrpNum", ECObjects.IntegerType);
    			prop.IsReadOnly = true;
    			keysClass.Add(prop);
    			//schema.AddClass(keysClass); // not necessary - gets added with dataClass
    
    			prop = new ECProperty("Keys", keysClass);
    			dataClass.Add(prop);
    
    			var vProp = new ECProperty("Version", ECObjects.StringType);
    			vProp.DisplayLabel = "Version";
    			vProp.IsReadOnly = true;
    			dataClass.Add(vProp);
    			// dataClass now contains nested ValuesClass, nested KeysClass, and non-nested Version property.
    			schema.AddClass(dataClass);
    #if DEBUG
    			ECSchemaXmlStringWriter xmlWriter = new ECSchemaXmlStringWriter();
    			string xml = xmlWriter.Serialize(schema); // to view schema as XML during Debugging
    #endif
    			ImportSchemaOptions iOpts = new ImportSchemaOptions((ushort)DgnECProviderId.ECXData);
    			SchemaImportStatus stat;
    			if (SchemaImportStatus.Success != (stat = DgnECManager.Manager.ImportSchema(schema, Session.Instance.GetActiveDgnFile(), iOpts)))
    			{
    				return false;
    			}
    
    			Element elm = Session.Instance.GetActiveDgnModel().FindElementById(existingElmId);
    			DgnECInstanceEnabler enabler = DgnECManager.Manager.ObtainInstanceEnabler(Session.Instance.GetActiveDgnFile(), dataClass);// schema.GetClass("GadgetStuff"));
    			StandaloneECDInstance wip = new StandaloneECDInstance(dataClass);
    			IDgnECInstance instGadget = enabler.CreateInstanceOnElement(elm, wip, false); // instance does NOT own element
    			instGadget["Values.Tag"].StringValue = "FirstGadget";
    			instGadget["Values.Inventor"].StringValue = "aSa";
    			instGadget["Keys.GrpNum"].IntValue = 10;
    			instGadget["Version"].StringValue = verMajor.ToString() + "." + verMinor.ToString();
    			instGadget.WriteChanges();
    
    			FindInstancesScope scope = FindInstancesScope.CreateScope(Session.Instance.GetActiveDgnFile(), new FindInstancesScopeOption(DgnECHostType.Element, false));
    
    			// This query and FindInstances works fine. The WhereClause is set up to look for the non-nested Version property.
    			var query = new ECQuery(dataClass);
    			PropertyExpression exp = new PropertyExpression(RelationalOperator.EQ, dataClass["Version"], "1.0");
    			query.WhereClause.Add(exp);
    			query.SelectClause.SelectAllProperties = true;
    
    			var dgnInstances = DgnECManager.Manager.FindInstances(scope, query);
    			if (null != dgnInstances && 0 < dgnInstances.Count())
    			{
    				foreach (var inst in dgnInstances)
    				{
    					string msg = string.Format("Tag = {0}\nInventor = {1}\n", inst["Values.Tag"].StringValue, inst["Values.Inventor"].StringValue);
    					MessageBox.Show(msg, "By NON-Nested Property");
    				}
    			}
    
    			// I can NOT get this query and FindInstances to work at all. The WhereClause is set up to look for a property in a nested (struct) class.
    			// I have tried a lot of ways, some I have shown commented here, adding multiple classes to the query, etc., it just does not seem to work.
    			// Is this a bug or am I doing something wrong??
    			query = new ECQuery(dataClass);
    			//exp = new PropertyExpression(RelationalOperator.EQ, dataClass["Keys.GrpNum"], 10); // does not work -> Exception: dataClass["Keys.GrpNum"] is null
    			//exp = new PropertyExpression(RelationalOperator.EQ, dataClass["Values.Inventor"], "aSa); // does not work -> Exception: dataClass["Values.Inventor"] is null
    			//exp = new PropertyExpression(RelationalOperator.EQ, keysClass["GrpNum"], 10); // does not work -> just doesn't find anything
    			//exp = new PropertyExpression(RelationalOperator.EQ, valClass["Inventor"], "aSa"); // does not work -> just doesn't find anything
    
    			//List<IECClass> classes = new List<IECClass>();
    			//foreach (IECClass ecClass in schema.GetClasses())
    			//	classes.Add(ecClass);
    			//query = new ECQuery(classes);
    
    			foreach (var p in schema.GetProperties())
    			{
    				if (p.Name == "Inventor")
    				{
    					prop = p as ECProperty;
    					break;
    				}
    			}
    			
    			try
    			{
    				exp = new PropertyExpression(RelationalOperator.EQ, prop, "aSa"); // does not work -> just doesn't find anything
    				query.WhereClause.Add(exp);
    				//var where = QueryHelper.CreateWhereCriteriaForStringFilter(prop, "aSa", RelationalOperator.LIKE); // does not work - just doesn't find anything
    				//query.WhereClause.Add(where);
    				//QueryHelper.WherePropertyExpressions(query, "Inventor", RelationalOperator.EQ, "aSa"); // does not work - just doesn't find anything
    			}
    			catch (Exception e)
    			{
    				string msg = e.Message;
    			}
    			query.SelectClause.SelectAllProperties = true;
    
    			dgnInstances = DgnECManager.Manager.FindInstances(scope, query);
    			if (null != dgnInstances && 0 < dgnInstances.Count())
    			{
    				foreach (var inst in dgnInstances)
    				{
    					string msg = string.Format("Tag = {0}\nInventor = {1}\n", inst["Values.Tag"].StringValue, inst["Values.Inventor"].StringValue);
    					MessageBox.Show(msg, "By Nested Property");
    				}
    			}
    			else
    			{
    				MessageBox.Show("Well, THAT didn't work!", "By Nested Property");
    				return false;
    			}
    
    			return true;
    		}
    

  • 4 years ago Jan said:

    but the formalized query only seems work if the WhereClause property is in the "main" ECClass, if the property is in a class or struct that is nested in the main class, it does not work.

    Or maybe I am doing something wrong trying to set up the formalized query, to drill into the nested classes for the matching properties? It seems we should be able to tell the WhereClause the path to the desired property - using an access string or some other way?

    Is there a way to do this? Am I missing something?

    (My apologies for bad format or "weird" explanations, I don't use this resource very much)

  • Is there a way to do this?

    It must be answered by some body from Bentley. But I do not recall such example, so I am in doubts it is possible. 

    ECQuery is moře like object graph, not SQL with Option similar to joins etc.

    if the property is in a class or struct that is nested in the main class

    Although "nested" describes the situation well, from EC perspektivÄ› no such things (in my opinion) like nesting exists. Whatever is in EC class is the class property, not important it is primitive or struct type.

    we should be able to tell the WhereClause the path to the desired property

    Shoul be? It does not work in MicroStation API :-)

    My approach, as I wrote earlier, is simpler and moee straightforward (more primitive) : to obtain EC classes filteredd some basic EC query and to evaluace what is inside by custom code. Maybe not elegant, but works.

    Regards, 

      Jan 

  • Right thank you. I realize we can get all instances and then use linq or loop to find what we need. I just hoped it would be more efficient to grab 3 matching instances from the dgn rather than grab 5000 instances and then filter for matches.

    Although "nested" describes the situation well, from EC perspektivÄ› no such things (in my opinion) like nesting exists. Whatever is in EC class is the class property, not important it is primitive or struct type.

    Maybe, but we can't really setup a WhereClause to match the whole struct object when we only want to match one included property.

    Thanks for your insight... maybe someone from Bentley can confirm that it can't be done, or show a way to do it?

  • we can't really setup a WhereClause to match the whole struct object when we only want to match one included property

    It's disappointing that you can't do this.  We've been told repeatedly by the Bentley developers that an EC query is similar to an SQL query. If it's similar only to the most basic sort of SQL query then that is unfortunate.

    maybe someone from Bentley can confirm that it can't be done, or show a way to do it?

    Mangesh Shelar is the go-to developer for the EC query engine.  He's been helpful in the past, but we haven't seen him recently on this Forum.

     
    Regards, Jon Summers
    LA Solutions

Reply Children
No Data