ORDER BY C # Enum Флаги большинства матчей - PullRequest
2 голосов
/ 16 октября 2019

С учетом следующего перечисления:

[Flags]
public enum CheeseCharacteristic 
{
    Yellow = 1,
    Stinks = 2,
    Squeaks = 4,
    Holey = 8,
    Mouldy = 16,
    UseOnToast = 32
}

И объекта Cheese, обладающего свойством Characteristic, возможно ли заказать Сыры на основе того, сколько флагов сопоставлено с Characteristic? Например, скажем, Stilton имеет значение Characteristic Yellow, Stinks, Mouldy, а MildCheddar имеет значение Characteristic Yellow, UseOnToast, можно ли заказатьколичество совпадений для данного запроса?

Так что, если я хочу сравнить набор Cheese сущностей с Yellow, Mouldy, тогда возвращаются и Stilton, и MildCheddar, ноStilton - верхняя часть списка, так как в нем 2 совпадения.

Чтобы продолжить, если я сохраню значение перечисления в SQL Server, могу ли я выполнить эту сортировку в БД с помощью EF Core?

Ответы [ 3 ]

0 голосов
/ 16 октября 2019

Мне нравится эта версия, потому что она поддерживает безопасность типов при работе с любым перечислением битовой маски.

using System;
using System.Linq;
using System.Collections.Generic;



namespace SO_58416947_count_bitmask_flags {
    static class Program {

        [Flags]
        public enum CheeseCharacteristics {
            Yellow = 1,
            Stinky = 2,
            Squeaky = 4,
            Holey = 8,
            Mouldy = 16,
            Spreadable = 32,
            UseOnToast = Yellow | Spreadable
        }


        static void Main(string[] args) {
            List<CheeseCharacteristics> _testCases = new List<CheeseCharacteristics>() {
                {CheeseCharacteristics.UseOnToast | CheeseCharacteristics.Stinky},
                {CheeseCharacteristics.Yellow | CheeseCharacteristics.Holey},   //  Swiss
                {CheeseCharacteristics.Squeaky},
                {CheeseCharacteristics.Mouldy | CheeseCharacteristics.Spreadable}
            };

            List<CheeseCharacteristics> cases = _testCases.OrderByFlagCount();

            foreach(CheeseCharacteristics c in cases) {
                Console.WriteLine($"{c}");
            }
        }   //  Main()



        private static List<T> GetIndividualFlagValues<T>() where T:Enum {
            Type enumType = typeof(T);

            if (!enumType.IsEnum) { throw new ArgumentException("Must pass an enum type."); }

            List<T> result = new List<T>();

            foreach(T item in Enum.GetValues(enumType)) {
                result.Add(item);
            }

            return result;
        }   //  GetIndividualFlagValues()



        //extension method
        public static List<T> OrderByFlagCount<T>(this List<T> list) where T:Enum {
            List<T> flags = GetIndividualFlagValues<T>();
            List<T> results = list
                .OrderBy(t => flags.Where(f => true == t.HasFlag(f)).Count())
                .ToList();

            return results;
        }

    }   //  Program
}       //  ns
0 голосов
/ 16 октября 2019

Чтобы сделать это в SQL, вы можете (не протестировано и не оптимизировано):

Использовать код в этой записи блога , чтобы создать функцию для преобразования int в строковое представлениедвоичное представление типа int:

CREATE FUNCTION dbo.Int2Binary (@i INT) RETURNS NVARCHAR(16) AS BEGIN
    RETURN
        CASE WHEN CONVERT(VARCHAR(16), @i & 32768 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i & 16384 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &  8192 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &  4096 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &  2048 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &  1024 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &   512 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &   256 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &   128 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &    64 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &    32 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &    16 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &     8 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &     4 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &     2 ) > 0 THEN '1' ELSE '0'   END +
        CASE WHEN CONVERT(VARCHAR(16), @i &     1 ) > 0 THEN '1' ELSE '0'   END
END;
GO

И следуйте этому ответу , чтобы создать функцию для подсчета вхождений строки в подстроку.

CREATE FUNCTION dbo.CountOccurrencesOfString
(
    @searchString nvarchar(max),
    @searchTerm nvarchar(max)
)
RETURNS INT
AS
BEGIN
    return (LEN(@searchString)-LEN(REPLACE(@searchString,@searchTerm,'')))/LEN(@searchTerm)
END

Изатем смешайте их вместе, как арахисовое масло и желе:

SELECT * FROM Cheeses
ORDER BY CountOccurrencesOfString('1', Int2Binary(Characteristics))
0 голосов
/ 16 октября 2019

Это должно работать , но не с EF

public class Cheese
{
    public string Name { get; }

    public CheeseCharacteristic Characteristic { get; }

    public Cheese(string name, CheeseCharacteristic characteristic)
    {
        Name = name;
        Characteristic = characteristic;
    }
}

public class Program
{
    private readonly static int[] CheeseCharacteristicValues =
        Enum.GetValues(typeof(CheeseCharacteristic)).Cast<int>().ToArray();

    public static void Main()
    {
        var pattern = CheeseCharacteristic.Yellow | CheeseCharacteristic.Mouldy;

        var cheeses = new List<Cheese>()
        {
            new Cheese("Camembert", CheeseCharacteristic.Stinks),

            new Cheese("MildCheddar", 
                CheeseCharacteristic.Yellow | CheeseCharacteristic.UseOnToast),

            new Cheese("Stilton", 
                CheeseCharacteristic.Yellow | CheeseCharacteristic.Stinks |
                CheeseCharacteristic.Mouldy),
        };

        IEnumerable<Cheese> sortedCheeses = 
            cheeses.OrderByDescending(c => GetFlagCount(c.Characteristic & pattern));
        ...
    }

    private static int GetFlagCount(CheeseCharacteristic characteristic) => 
        CheeseCharacteristicValues.Count(v => ((int)characteristic & v) == v);
}

Обратите внимание, что значения перечисления извлекаются в статическую переменную, чтобы избежать их извлечения (и создания экземпляра массива) каждый раз *Выполнено 1006 *.

Ссылка на скрипку

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...