Reputation: 1041
I'm using Microsoft.Azure.Search version 3.0.1,
I'm trying the following:
// subset of my index's fields
private class SyncFields
{
public string Id { get; set; }
public DateTimeOffset? ApprovedOn { get; set; }
public DateTimeOffset? IgnoredOn { get; set; }
}
public void Sync()
{
var sync = new SyncFields
{
Id = "94303",
ApprovedOn = null,
IgnoredOn = DateTime.UtcNow
};
var searchClient = new SearchServiceClient("xxxx",
new SearchCredentials("xxxx"));
searchClient.SerializationSettings.NullValueHandling = NullValueHandling.Include;
using (var client = searchClient.Indexes.GetClient("xxxx"))
{
client.SerializationSettings.NullValueHandling = NullValueHandling.Include;
var batch = IndexBatch.Merge<SyncFields>(new[] { sync });
client.Documents.Index<SyncFields>(batch);
}
}
This isn't settings ApprovedOn
to null. It ignores it. If I set a non-null value, it does set it.
According to the documentation here the merge operation updates the field to be null. And in fact, if I make this Http post request manually with JSON, this is true. But the SDK isn't updating the field(s) to null. What am I missing?
Upvotes: 4
Views: 1470
Reputation: 1041
I found the culprit in the Azure Search SDK source.
Line 51, settings.NullValueHandling = NullValueHandling.Ignore;
is overriding the setting I tried to set. I will probably be making an issue about this in Github.
For now, I am using a custom converter as a workaround.
public class DefaultDateTimeOffsetIsNullConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(DateTimeOffset?));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var date = (DateTimeOffset?)value;
if (date == default(DateTimeOffset))
{
writer.WriteNull();
}
else
{
writer.WriteValue(date);
}
}
public override bool CanRead => false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
As in
var sync = new SyncFields
{
Id = "94303",
ApprovedOn = default(DateTimeOffset), // set to null
IgnoredOn = DateTime.UtcNow
};
// ...
client.SerializationSettings.Converters.Add(new DefaultDateTimeOffsetIsNullConverter());
// ...
Edit:
Two other superior options listed by Bruce: using Document which is untyped, and using the JsonPropertyAttribute on the field to get the correct serialization. Using Document is ideal for my use case, no serialization problem or custom converters:
var sync = new Document
{
["Id"] = "94303",
["ApprovedOn"] = null,
["IgnoredOn"] = null
};
// ... the same as before:
var batch = IndexBatch.Merge(new[] { sync });
await client.Documents.IndexAsync(batch);
Upvotes: 2
Reputation: 8634
This is a known limitation of the typed overloads of the Index
family of methods. The issue is described in detail here: https://github.com/Azure/azure-sdk-for-net/issues/1804
Some workarounds:
Index
instead for merge scenarios.Upload
instead of Merge
.[JsonProperty(NullValueHandling = NullValueHandling.Include)]
on the properties of your model class that you need to explicitly set to null in a merge operation (not recommended if you have many fields in your index).Upvotes: 4