DbSet.FirstOrDefault ()? - PullRequest
       2

DbSet.FirstOrDefault ()?

7 голосов
/ 22 ноября 2011

Я пытаюсь сделать это, но он говорит, что не может использовать FirstOrDefault,

public static int GetId(this Context db, Type type, string name)
{
    return db.Set(type).FirstOrDefault(x => x.Name == name).Id;
}

Ошибка: ' System.Data.Entity.DbSet' не содержитопределение для 'FirstOrDefault' и отсутствие метода расширения 'FirstOrDefault', принимающего первый аргумент типа 'System.Data.Entity.DbSet' (вы пропустили директиву using или ссылку на сборку?)

Затем я попробовал этот Cast метод, но он дал ошибку Невозможно создать DbSet из неуниверсального DbSet для объектов типа 'WindowStyle' (кстатиWindowStyle наследуется от DomainEntity ниже),

var set = db.Set(type).Cast<DomainEntity>();
return set.FirstOrDefault(x => x.Name == name).Id;

Вот класс,

public class DomainEntity
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
}

Ответы [ 6 ]

24 голосов
/ 28 апреля 2013

Может быть, вы пропали без вести

using System.Linq;
8 голосов
/ 22 ноября 2011

Этот ответ, возможно, не поможет вам в зависимости от того, насколько «динамична» ситуация, когда вы вызываете этот метод, в основном, если вы знаете тип во время компиляции или нет. Если вы знаете это, вы можете написать общий метод:

public static class MyExtensions
{
    public static int? GetId<TEntity>(this Context db, string name)
        where TEntity : DomainEntity
    {
        return db.Set<TEntity>()
            .Where(x => x.Name == name)
            .Select(x => (int?)x.Id)
            .FirstOrDefault();
    }
}

Я изменил его на проекцию, потому что вам не нужно загружать полную сущность, если вам нужен только Id. Вы можете позволить базе данных выполнить выбор свойства, чтобы немного повысить производительность. Также я возвращаю nullable int в случае, если в базе данных нет совпадений с именем.

Вы можете назвать это в своем коде так:

int? id = db.GetId<WindowStyle>("abc");

Как видно из этого решения, вы должны указать тип WindowStyle во время компиляции.

Это предполагает, что DomainEntity не является частью вашей модели (нет DbSet<DomainEntity>), а является просто базовым классом ваших сущностей. В противном случае решение Пола Кейстера было бы проще.

Редактировать

В качестве альтернативы вы также можете попробовать следующее:

public static class MyExtensions
{
    public static int? GetId(this Context db, Type entityType, string name)
    {
        return ((IQueryable<DomainEntity>)db.Set(entityType))
            .Where(x => x.Name == name)
            .Select(x => (int?)x.Id)
            .FirstOrDefault();
    }
}

И назовите это:

int? id = db.GetId("abc", someType);

Будет сгенерировано исключение во время выполнения, если someType не наследуется от DomainEntity. Универсальная версия проверит это во время компиляции. Итак, если вы можете предпочесть первую версию.

5 голосов
/ 22 ноября 2011

Первая конструкция не будет работать, потому что вы работаете с неуниверсальным DbSet, поэтому вы не можете применить метод расширения FirstOrDefault, который работает только с универсальным. Похоже, вы уже это понимаете, потому что вы уже пытаетесь получить неуниверсальный DbSet. Ошибка, которую вы получаете с помощью метода Cast (), вызвана вашей попыткой привести DbSet к DbSet. Этого нельзя допустить, потому что если бы это было возможно, можно было бы добавить несоответствующие элементы в DbSet (объекты типа, отличного от WindowsStyle). Еще один способ сказать, что Covariance не поддерживается для DbSets, поскольку DbSets допускает добавления.

Я думаю, вам придется найти другой способ сделать то, что вы пытаетесь сделать. Смешивание LINQ и наследования таким образом, очевидно, проблематично. Поскольку у вас определен базовый тип, и вы работаете только с атрибутами, которые доступны для базового типа, почему бы просто не запросить базовый тип?

    public static int GetId(this Context db, string name)
    {
        return db.DomainEntities.FirstOrDefault(x => x.Name == name).Id;
    }

Возможно, вас беспокоит конфликт имен между различными типами, но вы можете начать с этого и посмотреть на связи производных типов, чтобы убедиться, что вы смотрите на правильный тип. Один из способов справиться с этим - добавить флаг типа в определение DomainEntity.

3 голосов
/ 22 ноября 2011

Вот проблема.Класс DbSet имеет собственную реализацию Cast<T>(), которая ТОЛЬКО допускает типы баз данных (например, Cast<WindowStyle>()), поэтому этот метод не допускает Cast<DomainEntity>() и вызывает исключение.

Вместо этого вы хотите использовать метод расширения IQueryable.Cast<T>(), который просто приведёт ваши данные к базовому типу.Вот пример:

var set = ((IQueryable)db.Set(type)).Cast<DomainEntity>();
return set.First(x => x.Name == name).Id;
2 голосов
/ 22 ноября 2011

Вот моя идея, которая, кажется, работает.

public static int GetId(this Context db, Type type, string name)
{
    var set = db.Set(type);
    foreach (dynamic entry in set)
        if (entry.Name == name)
            return entry.Id; 
}
0 голосов
/ 22 ноября 2011

Попробуйте немного поменять местами, сделайте

set.Where(x => x.Name == name).Select(o=>o.Id).FirstOrDefault();

Ваш вернет нулевую сущность, а затем попытается получить Id из нее.

...