Search code examples
c#androidsqlite-net-extensions

NullReferenceException Query SQLite database with Where on a concatenated string property


I'm trying to select a record using the following code:

Location item = connection
  .Table<Location>()
  .Where(l => l.Label.Equals(label))
  .FirstOrDefault();

This results in:

System.NullReferenceException: Object reference not set to an instance of an object.

When I try the same, on a different property (Postcode), it all works fine also when no records are found.:

Location item = connection
  .Table<Location>()
  .Where(l => l.Postcode.Equals(label))
  .FirstOrDefault();

This is the Location Class:

// These are the Locations where the Stock Take Sessions are done
public class Location : DomainModels, IComparable<Location>
{
    [JsonProperty("id"), PrimaryKey]
    public int Id { get; set; }
    public string Name { get; set; }
    public string Street { get; set; }
    public int Number { get; set; }
    public string Postcode { get; set; }
    public string City { get; set; }
    public bool Completed { get; set; }

    [Ignore] // Removing this does not have an impact on the NullReferenceException
    public string Label => $"{Name ?? ""} - ({Postcode ?? ""})";

    public int CompareTo(Location other)
    {
        return Name.CompareTo(other.Name);
    }

    // Navigation property
    // One to many relationship with StockItems
    [OneToMany(CascadeOperations = CascadeOperation.All), Ignore]
    public List<StockItem> StockItems { get; set; }

    // Specify the foreign key to StockTakeSession
    [ForeignKey(typeof(StockTakeSession))]
    public int StockTakeSessionId { get; set; }

    // One to one relationship with StockTakeSession
    [OneToOne]
    public StockTakeSession StockTakeSession { get; set; }

}

What am I doing wrong?

Thanks for any suggestions!


Solution

  • Your where filters in the data store on Label but your markup on your class Location has decorated the Label property with IgnoreAttribute. This means the Label property will not be set until after the entity has been materialized to memory and you can't do anything with it in the data store.

    .Where(l => l.Label.Equals(label))
    

    Fixes

    There are some options.

    • You could set this to computed and create a computed column in the store with that same logic. This involves manually changing your table schema either directly in your RDBMS manager or editing your migration scripts. The property gets marked with [DatabaseGenerated(DatabaseGeneratedOption.Computed)] (if using attributes, which your code above is).
    • You could change the Where to filter on the Properties that compose Label that are found in the store. ie: .Where(l => l.Postcode.Equals(Postcode) && l.Name.Equals(Name))
    • You could materialize everything before that particular filter to memory and then apply the filter. This is not recommended if everything up to that point leads to a lot of records. Example, with the code below if the table is large you would be retrieving everything for a single record.

      Location item = connection
        .Table<Location>()
        .AsEnumerable()
        .Where(l => l.Label.Equals(label))
        .FirstOrDefault();
      

    Edit

    [Ignore] // Removing this does not have an impact on the NullReferenceException

    No, it should not unless you go through and add the column with the same name to your existing schema and populate it with all data. (or create a computed column in your schema with the same name)