Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-4852

Linq3Provider does not deal correctly with non-existing objects

    • Type: Icon: Bug Bug
    • Resolution: Works as Designed
    • Priority: Icon: Unknown Unknown
    • None
    • Affects Version/s: None
    • Component/s: None
    • None
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?

      Summary

      Especially notable during projection, if you check whether a field exists or not by doing a null-check, or by using string.IsNullOrEmpty or similar, it will return as not null when the field, or a parent of that field does not exist.

      This is a breaking change in version 2.22.0 of the driver, coming from 2.18.0.

      How to Reproduce

      Add this to a collection:

       

      {
        "_id": { "$oid": "000000000000000000000001" },
        "Name": "Example Name",
        "SecretValue": "Some Secret Value"
      }
      

      With the following classes:

       

       

      public class MainClass
      {
          [BsonId]
          public ObjectId Id { get; set; }
          public string Name { get; set; }
          public string SecretValue { get; set; }
          public OptionalClass OptionalData { get; set; }
      }
      
      public class OptionalClass
      {
         public string PublicData { get; set; }
         public string PrivateData { get; set; }
      }
      
      public class ProjectedMainClassWithoutSecretData
      {
          [BsonId]
          public ObjectId Id { get; set; }
          public string Name { get; set; }
          public ProjectedOptionalClassWithoutSecretData OptionalData { get; set }
          public bool HasOptionalPrivateData { get; set; }
      }
      
      public class ProjectedOptionalClassWithoutSecretData
      {
          public string PublicData { get; set; }
      }
      

      Then do a find like so:

       

      async Task<ProjectedMainClassWithoutSecretData> GetPublicData(IMongoCollection<MainClass> collection, ObjectId itemId)
      {
          var filter = Builders<MainClass>.Filter.Eq(x => x.Id, itemId);
          var projection = Builders<MainClass>.Projection.Expression(main => new ProjectedMainClassWithoutSecretData() {
            Id = main.Id,
            Name = main.Name,
            OptionalData = main.OptionalData != null ? new ProjectedOptionalClassWithoutSecretData() {
               PublicData = main.OptionalData.PublicData
            } : null,
            HasOptionalPrivateData = !string.IsNullOrEmpty(main.OptionalData.PrivateData)
          });
          var result = await collection.Find(filter).Project(projection).FirstOrDefaultAsync();
          return result;
      }

      You will find that the result will have OptionalData set, and HasOptionalPrivateData set to true, where null and false were expected.

       

       

      Additional Background

      From what I can tell, there is no easy work-around to check for unset fields, and when dealing with larger classes, manual projection is not maintainable.

      Please provide any additional background information that may be helpful in diagnosing the bug.

            Assignee:
            robert@mongodb.com Robert Stam
            Reporter:
            sgrevelink@gmail.com Sjoerd Grevelink
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: