I receive a number of different messages that I deserialize into very dissimilar objects
Most of these objects have non-nullable fields, but across all the message types (From GraphQL, Azure service- and storage-bus, to CosmosSB and probably more that I am forgetting) some of them are happy to deserialize into objects with null fields anyway.
As such, I could very much use a component that will let me do
T objectFromMessage = GetObject(message);
FieldAsserter.AssertAllFieldsSet<T>(objectFromMessage); //Throws if any non-nullable fields are null
Do you know if any such field validation method already exists, either by default or somewhere in Nuget? Alternatively, if I have to write it myself, can you give me any clues re. where to start looking?
What did you try and what were you expecting?
Nothing really, at this point - A cursory search of NuGet and a few thoughts about reflection is as far as I got before thinking "Maybe SO has an answer."
There are a couple of similar questions on SO already, but they appear to work for single objects - the answers are of type "write an extension method to your class that does it" but I would like a generic method that'll work for anything - I have too many message types.
As an alternative, if you only want to check public properties that are NOT nullable (e.g. string
rather than string?
) you can filter out the nullable properties and check only the non-nullable ones like so:
(NOTE: This requires .NET 6.0 or later.)
public static void RequiresPropertiesNotNull(object item, string name)
{
if (item is null)
throw new ArgumentNullException(nameof(item), "Item being checked cannot be null.");
name ??= "<Unknown>"; // Don't want to throw if name is null, because it would hide the thing we're checking for nulls.
var allProps = item.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
var nonNullableProps =
from prop in allProps
where
prop.CanRead
&& !prop.PropertyType.IsValueType
&& !IsNullable(prop)
select prop;
foreach (var prop in nonNullableProps)
{
object? value = prop.GetValue(item);
if (value is null)
throw new InvalidOperationException($"Property {prop.Name} for {name} of type {item.GetType().FullName} cannot be null.");
RequiresPropertiesNotNull(value, name);
}
}
public static bool IsNullable(PropertyInfo prop)
{
var context = new NullabilityInfoContext();
var info = context.Create(prop);
return info.WriteState is NullabilityState.Nullable;
}
Note that this is recursive, so it will check all the public properties and (recursively) all of their public properties and so on.
It does NOT check for self-referential objects, so if you try to use this to check an object graph that contains cycles, you WILL get a stack overflow error.
You might use it as follows: Given class Outer
and class Inner
where the Inner
class contains a property CanBeNull
which is allowed to be null and an InnerName
property that is not allowed to be null:
public sealed class Outer
{
public string OuterName { get; set; }
public Inner Inner { get; set; }
}
public sealed class Inner
{
public string InnerName { get; set; }
public string? CanBeNull { get; set; }
}
Then in the following code, the second call to RequiresPropertiesNotNull()
will throw an exception:
public static void Main()
{
var innerOk = new Inner { InnerName = "InnerOK" };
var innerBad = new Inner();
var outerOk = new Outer { OuterName = "OuterOK", Inner = innerOk };
var outerBad = new Outer { OuterName = "OuterBad", Inner = innerBad };
RequiresPropertiesNotNull(outerOk, nameof(outerOk)); // OK
RequiresPropertiesNotNull(outerBad, nameof(outerBad)); // Throws
}
If you want to remove the recursive part, just remove the line of code RequiresPropertiesNotNull(value, name);
in the RequiresPropertiesNotNull()
implementation.