[CONNECT .NET] ECQuery

I'd like to compose a query to use with DgnECManager.FindInstances using an ECQuery.  Unfortunately, the MstnPlatformNet documentation continues to ignore that class.  The SDK examples help little, using the catch-all SelectAllProperties.

ECQuery query = new ECQuery(GetSearchClasses());
query.SelectClause.SelectAllProperties = true;

How can one write a query, say, to select Item instance properties having a certain value?   That is, I'd like to know about ECQuery.SelectClause. If Item Types supported SQL, I would write something like...

SELECT * FROM MyItemType
WHERE ID='abc'

Since DgnECManager.FindInstances returns IQueryable<> it's tempting to ignore the MstnPlatformNet  API and use LINQ to compose a query.  Which approach would you make your strategy?

  • Hi ,

    FWIW. We are actively looking to packages some more EC API documentation (in current form) in the MicroStation SDK and once delivered we can try to advance EC API docs with appropriate Getting Started info and example(s), and possible "Explorer" app.  I have been trying to introduce these concepts more and more as we move things forward.  Thank you for your always helpful code snips and posts!

    Bob



  • For comparison, and possibly inspiration, here's how I create an ECQuery using C++...

    bool  ECQueryFactory::ComposeCriteria  (ECQueryPtr  query,
                                            WCharCP     property,
                                            WCharCP	    filter)
    {
      using namespace Bentley::DgnPlatform;
      //	Property 'all' is a pseudo-property meaning 'find all instances'
      if (0 == wcsicmp (L"all", property))
        return true;
      const bool    AcceptAll       { (nullptr == filter || 0 == wcslen (filter) || L'*' == *filter) };
      bool          composed        { false };
      if (query.IsValid())
      {
        WhereExpressionPtr  expression      { WhereExpression::CreatePropertyExpression (property) };
        const bool&         IfMatch         { true };
        const bool&         CaseSensitive   { false };
        WhereCriterionPtr   where;
        WhereCriterion::StringFilterError  error;
        if (!AcceptAll)
        {
        if (RegEx::IsRegEx (filter))
        {
          where = WhereCriterion::CreateRegexFilter (&error, *expression, filter, CaseSensitive);
        }
        else
        {
          where = WhereCriterion::CreateStringFilter(&error, *expression, filter, IfMatch, CaseSensitive);
        }
      }
      if (!where.IsValid () && WhereCriterion::STRING_FILTER_ERROR_InvalidSyntax == error)
      {
        CMessageTracer  moan (__FUNCTION__ L": CreateStringFilter syntax error filter="); moan.Quote (filter);
        moan.Advisory ();
      }
      else
      {
        if (!AcceptAll)
        {
          query->SetWhereCriterion (*where);
        }
        composed = query.IsValid ();
        }
      }
      return composed;
    }
    

    Classes ECQuery, WhereExpression and WhereCriterion are documented in MicroStationAPI help. The documentation is terse, with no explanation about how to use those methods. But at least it's available!

     
    Regards, Jon Summers
    LA Solutions

  • I am trying to get whole bunch of .NET ECFramework documentation in U13 SDK

    Excellent!

    You can tell what relationships you want

    At this point I'm looking at simple queries that concern a single Item Type and its properties.  For example, suppose I want to query my AreaAnnotator Item Type, I want to write something similar to SQL statement...

    SELECT ID, RoomType, occupied FROM 'Area Feature'
    WHERE occupied = True AND RoomType = 'Office'

    That would evolve into a relationship when I want to get the area of the DGN element that is tagged with that Item Type...

    SELECT ID, RoomType, occupied, Element.Area FROM 'Area Feature', Element
    WHERE occupied = True AND RoomType = 'Office' AND Element.Area > 500

    FWIW I already wrote, with Paul Connelly's help, a C# demo that implements an EC relationship to harvest tag elements.

     
    Regards, Jon Summers
    LA Solutions

  • Hi Jon,

    I apologize for joining this thread late.

    About .NET ECQuery documentation: I am trying to get whole bunch of .NET ECFramework documentation in U13 SDK. Robert Hook will let you know accordingly.

    About using .NET ECQuery: 

    In ECQuery.SelectClause, 

    You call tell which properties you want: query.SelectClause.SelectedProperties.Add(..

    You can tell what relationships you want w.r.t. searchClasses as:

    QueryRelatedClassSpecifier relClassSpec = new QueryRelatedClassSpecifier (m_relationshipClass, false, ECI.RelatedInstanceDirection.Forward, m_targetClass, false);
    RelatedCriterion relatedPropertyCriterion = new RelatedCriterion (relClassSpec);

    query.SelectClause.SelectedRelatedInstances.Add (new RelatedInstanceSelectCriteria (relClassSpec, true));

    Please let me know if you are looking for something else.

    Thanks,

    Mangesh


    This is a test

  • We need public methods!

    There is a public method and its description is available in XML file as well. Or do you think my code above does not work despite of I wrote it works, because used method is private? ;-)

    And Visual Studio provides clarification one from overloaded methods is available:

    With regards,

      Jan

  • I recommend to read a description of QueryHelper.WherePropertyExpressions method (not in help, but in xml file)

    Unfortunately...

            <member name="M:Bentley.EC.Persistence.Query.QueryHelper.WherePropertyExpressions(System.Collections.Generic.IList{Bentley.ECObjects.Schema.IECClass},Bentley.EC.Persistence.Query.WhereCriteria,System.Object[])">
                <summary>Private implementation method.Search all classes in query
    

    We need public methods!

     
    Regards, Jon Summers
    LA Solutions

  • But far slower beats can't implement using ECQuery!

    Try it. Measure. ;-)

    More options are (nearly) always available with own pros and cons.

    The problem of LINQ is that it's slow by its nature (sometimes a difference from optimal solution is minor, but easily can be huge), unfortunately in the discussed situation LINQ represents wrong approach not because of its feature, but because it's requires to collect all data, pass them to managed API and evaluate/filter them later, which is really inefficient in my opinion.

    It seems not all options of native API are available in managed world, but it's alwyas possible to implement everything in native API and to pass results only to managed code using C++/CLI.

    Regards,

      Jan

  • I think LINQ will be far slower

    Perhaps.  But far slower beats can't implement using ECQuery!

     
    Regards, Jon Summers
    LA Solutions

  • Ok, after some testing, this code seems to work:

    FindInstancesScope scope2 = FindInstancesScope.CreateScope(Session.Instance.GetActiveDgnFile(), new FindInstancesScopeOption(DgnECHostType.All, false));
    IECSchema ecSchema = DgnECManager.Manager.LocateSchemaInScope(scope2, "DgnCustomItemTypes_MasterPlanner__x0020__", 1, 0, SchemaMatchType.Latest);
    string[] ecClassesNames = { "Something" };
    var query2 = QueryHelper.CreateQuery(ecSchema, ecClassesNames);
    QueryHelper.WherePropertyExpressions(query2, "ID", RelationalOperator.EQ, 1);
    query2.SelectClause.SelectAllProperties = true;
    var result2 = dgnECManager.FindInstances(scope, query2);

    As usually, you have to iidentify / locate:

    • EC schema ("DgnCustomItemTypes_MasterPlanner__x0020__")
    • EC class (or classes) you want to search ("Something")
    • EC property in the classes ("ID")
    • An evaluation criteria, which is "ID is equal to 1" in this case.

    I recommend to read a description of QueryHelper.WherePropertyExpressions method (not in help, but in xml file), because conditions/prerequisities are described here.

    With regards,

      Jan

    P.S. Please don't ask such questions! After initial thinking "it's really interesting question, it would be nice to know also" and some playing with code ... a half of a day is just missing :-))))

  • Hi Jon,

    Does it perform more efficient selection and sorting than using LINQ after  GetInstances?

    I do not know how exactly ECQuery is implemented (but a study of ECquery assemblies code provides some insight and ideas), but I think LINQ will be far slower ... because LINQ is always slow (especially when there is no knowledge how LINQ works internally and queries are not implemented in a right way and cannot be optimized internally) Slight smile

    My assumption is:

    When ECQuery with criteria is used, formalized query is passed to native API and processed very close to DGN data. It means the search is fast and only limited amount of data is returned back to native API.

    When LINQ is used to posprocess, evaluate and filter results from ECQuery, possible all data have to be passed (and marshalled) to managed API and even when optimizations like lazy instantiation (known from e.g. Entity Framework) will be used, to evaluate conditions and filter elements, the elements (and EC classes and any other data) have to be instantiated in NET (memory intensive) or marshalled from native to managed (processor intensive).

    With regards,

      Jan