How can I get the Primary Key for the DbSet?

.net-core c# entity-framework-core

Question

I use Net Core 1.1 and Entity Framework Core with the fluent API. I am writing a simple extension method for DbSet to show its entities in Console:

public static class DbSetExtension
{
    public static void Show<T>(this DbSet<T> set) where T:class
    {

        WriteLine();
        WriteLine($"Set: {typeof(T).Name} - {set.Count()} objects.");
        WriteLine();

        foreach (var e in set)
        {
            WriteLine(e);
        }

        WriteLine();
        WriteLine();
    }
}

This works but I'd like to have the entities sorted by the primary key before showing them. If I had the DbContext it would have been easily accomplished by doing something like this:

var entityType = db.Model.FindEntityType(typeof(T));
var primaryKeyName = entityType.FindPrimaryKey().Properties.First().Name;
var set = db.Set<T>();
var orderedEntities = (set.OrderBy(e=> e.GetType().GetProperty(primaryKeyName).GetValue(e,null))).ToList();

Is there a way to get the same result starting from the DbSet?

1
3
3/7/2017 3:32:18 PM

Accepted Answer

It's possible, but not "officially" - you have to use some methods marked as This API supports the Entity Framework Core infrastructure and is not intended to be used directly from your code. This API may change or be removed in future releases..

You basically utilize the fact that DbSet<T> implements IInfrastructure<IServiceProvider>, so you can obtain IDbContextServices via GetService method in order to get the model from Model property:

using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;

...
var model = set.GetService<IDbContextServices>().Model;
var entityType = model.FindEntityType(typeof(T));
var properties = entityType.GetProperties();
var primaryKeyName = entityType.FindPrimaryKey().Properties.First().Name;
var sortedSet = (set.OrderBy(e=> e.GetType().GetProperty(primaryKeyName).GetValue(e,null))).ToList();
...
4
3/7/2017 1:28:43 PM

Popular Answer

I accepted Ivan Stoev's solution because it works so it is a very good answer to my question.

But as Ivan pointed out GetService and IDbContextService API are marked as "This API supports the Entity Framework Core infrastructure and is not intended to be used directly from your code. This API may change or be removed in future releases.." and I prefer to not use them in my code.

An alternative approach that doesn't use internals and reflection would be to rewrite the extension method to accept the sorting property as a parameter.This would also increase its flexibility.

I am posting this alternative solution because it may be useful to others with the same problem:

using System;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using static System.Console;

public static class DbSetExtension
{
    public static void ShowSortedBy<T,TKey>(this DbSet<T> set, Func<T,TKey> keySelector) where T:class
    {
        var sortedSet = set.OrderBy(x => keySelector(x)).ToList();

        WriteLine();
        WriteLine($"Set: {typeof(T).Name} - {set.Count()} objects.");
        WriteLine();

        foreach (var e in sortedSet)
        {
            WriteLine(e);
        }

        WriteLine();
        WriteLine();

    }
}


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow