Ben
Ben

Reputation: 2020

Dynamic Linq Core OrderBy Nullable Child Property

When I try to use the System.Linq.Dynamic.Core library to order a list, I am getting a NullReferenceException.

var myFinalOrderedList = myList.AsQueryable()
    .OrderBy("Company.Name asc, Process.Name asc, Reference.Name asc")
    .ToList();

The null exception is occurring on the Reference.Name OrderBy because Reference is nullable.

How do I order by the nullable Reference object?

StackTrace (Sensitive info replaced with "****"):

    {
  "ClassName": "System.NullReferenceException",
  "Message": "Object reference not set to an instance of an object.",
  "Data": null,
  "InnerException": null,
  "HelpURL": null,
  "StackTraceString": "   at lambda_method(Closure , ControlActivity )\r\n   at System.Linq.EnumerableSorter`2.ComputeKeys(TElement[] elements, Int32 count)\r\n   at System.Linq.EnumerableSorter`2.ComputeKeys(TElement[] elements, Int32 count)\r\n   at System.Linq.EnumerableSorter`2.ComputeKeys(TElement[] elements, Int32 count)\r\n   at System.Linq.EnumerableSorter`1.ComputeMap(TElement[] elements, Int32 count)\r\n   at System.Linq.EnumerableSorter`1.Sort(TElement[] elements, Int32 count, Int32 minIdx, Int32 maxIdx)\r\n   at System.Linq.OrderedEnumerable`1.GetEnumerator(Int32 minIdx, Int32 maxIdx)+MoveNext()\r\n   at System.Collections.Generic.List`1.AddEnumerable(IEnumerable`1 enumerable)\r\n   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)\r\n   at ****.Dal.Repositories.ControlActivities.ControlActivityRepo.Search(ControlActivitySearchModel input) in C:\\Users\\****\\source\\Workspaces\\****\\Main\\****\\****.Dal\\Repositories\\ControlActivities\\ControlActivityRepo.cs:line 1189\r\n   at ****.Application.ControlActivities.ControlActivityService.Search(ControlActivitySearchModel input) in C:\\Users\\****\\source\\Workspaces\\****\\Main\\****\\****.Application\\ControlActivities\\ControlActivityService.cs:line 339\r\n   at ****.Api.Controllers.ControlActivityController.Search(String apikey, ControlActivitySearchModel input) in C:\\Users\\****\\source\\Workspaces\\****\\Main\\****\\****.Api\\Controllers\\ControlActivityController.cs:line 117",
  "RemoteStackTraceString": null,
  "RemoteStackIndex": 0,
  "ExceptionMethod": null,
  "HResult": -2147467261,
  "Source": "Anonymously Hosted DynamicMethods Assembly",
  "WatsonBuckets": null
}

Company, Process, and Reference are Foreign Keys to the main list (ControlActivity). All three pretty much just have PK Id and NVARCHAR Name. Both Company and Process are required for all control activities and Reference is not required.

Upvotes: 2

Views: 1440

Answers (1)

Ben
Ben

Reputation: 2020

I figured out the answer to my own question with some help from the link posted by @Alen and from https://github.com/StefH/System.Linq.Dynamic.Core/issues/98. Below, I am showing a bit more of my actual code which is why it looks different from question. The answer lies with the StaticMethod.ConvertToNullableNested. For example, I am passing in {i.ColumnName} {i.SortOrder} = "Company.Name asc" and it's returning Company == null ? null : Company.Name asc. I then append each of the sorts together and pass into the OrderBy.

//TURN INTO QUERYABLE
var finalList = unionList.AsQueryable();

//DYNAMIC SORT
if (input.SortModel?.SortModelItems?.Count > 0)
{
    string sortQry = String.Empty;
    foreach (var i in input.SortModel.SortModelItems)
    {
        sortQry = sortQry + $"{StaticMethod.ConvertToNullableNested($"{i.ColumnName} {i.SortOrder}")}, ";
    }
    sortQry = sortQry.TrimEnd(", ");
    finalList = finalList.OrderBy(sortQry);
}

//RETURN AND REMEMBER TO .TAKE(Pagesize)
return Tuple.Create(finalList.Take(input.PageSize).ToList(), finalRowCount);

public static string ConvertToNullableNested(string expression, string result = "", int index = 0)
    {
        //Transforms => "a.b.c" to "(a != null ? (a.b != null ? a.b.c : null) : null)"
        if (string.IsNullOrEmpty(expression))
            return null;
        if (string.IsNullOrEmpty(result))
            result = expression;
        var properties = expression.Split(".");
        if (properties.Length == 0 || properties.Length - 1 == index)
            return result;
        var property = string.Join(".", properties.Take(index + 1));
        if (string.IsNullOrEmpty(property))
            return result;
        result = result.Replace(expression, $"{property} == null ? null : {expression}");
        return ConvertToNullableNested(expression, result, index + 1);
    }

Upvotes: 1

Related Questions