redis / redis-om-dotnet

Object mapping, and more, for Redis and .NET
MIT License
458 stars 76 forks source link

Retrieving a hash that was set without a property present throws exeption on SendToJson #120

Closed diogonborges closed 2 years ago

diogonborges commented 2 years ago

Got this error when using Redis Enterprise on Azure

and running the following line:

The model is

[Document(IndexName = "asset_idx", Prefixes = new[] { nameof(Model) }, StorageType = StorageType.Hash)]
public class Model
{
    [Searchable(Sortable = true, Aggregatable = true, PropertyName = "Id")]
    public string Id { get; set; }

    ....

    [Searchable(Sortable = true, Aggregatable = true, PropertyName = "D_State")]
    public string D_State { get; set; }
}

The line of code that throws exception

var entry = _cacheCollection.FirstOrDefault(x => x.Id == "ARandomValidId");
System.Collections.Generic.KeyNotFoundException: The given key 'D_State' was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at Redis.OM.RedisObjectHandler.SendToJson(IDictionary`2 hash, Type t)
   at Redis.OM.RedisObjectHandler.FromHashSet[T](IDictionary`2 hash)
   at Redis.OM.Searching.SearchResponse`1..ctor(RedisReply val)
   at Redis.OM.Searching.RedisQueryProvider.ExecuteQuery[T](Expression expression)
   at Redis.OM.Searching.RedisQueryProvider.FirstOrDefault[TResult](Expression expression)
   at Redis.OM.Searching.RedisQueryProvider.Execute[TResult](Expression expression)
   at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)

This happens because I've stored a hash entry that does not contain that string property and then when retrieving, the Json conversion looks to be failing.

Thanks!

slorello89 commented 2 years ago

Hmm This doesn't quite look right 🤔 I tried replicating this behavior to no avail:

provider.Connection.CreateIndex(typeof(Model));
collection.Insert(new Model(){Id = "ARandomValidId"});
try
{
    collection.FirstOrDefault(x => x.Id == "ARandomValidId");
}
catch (Exception ex)
{
    Console.WriteLine(ex);
}

Is there something else I'm missing Maybe?

What version of .NET are you on? What version of redis OM are you using?

diogonborges commented 2 years ago

Hey @slorello89! After a lot of trial and testing I've found it! :O

if (hash.Any<KeyValuePair<string, string>>((Func<KeyValuePair<string, string>, bool>) (x => x.Key.StartsWith(propertyName))))

This causes a problem when we have multiple properties with the same start of the string propertyName.

Example:

[Document(IndexName = "employees_idx", StorageType = StorageType.Hash)]
public class Employee
{
    [Searchable(Sortable = true)] public string Name { get; set; }

    [Searchable(Aggregatable = true)] public string Location { get; set; }

    [Searchable(Aggregatable = true)] public int? LocationNumber { get; set; }
}
var  provider = new RedisConnectionProvider(RedisConnectionString);
var employees = provider.RedisCollection<Employee>();
provider.Connection.DropIndexAndAssociatedRecords(typeof(Employee));
provider.Connection.CreateIndex(typeof(Employee));
employees.Insert(new Employee { Name = "Bob", LocationNumber = 10});
var first = employees.FirstOrDefault(x=>x.Name == "Bob");