2011-11-21 7 views
5

Я пытаюсь сделать это, но он говорит, что я не могу использовать FirstOrDefault,DbSet.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» может быть найдено (вы пропали без вести с помощью директивы или ссылка на сборку?)

затем я попробовал этот метод, но Cast это сообщение об ошибке Невозможно создать DbSe т от необщего DbSet для объектов типа «Оформление_окна» (кстати 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; } 
} 
+9

ли вы 'используя System.Linq' в верхней части файла? DbSet наследует IEnumerable, но функции LINQ - это все методы расширения и не будут доступны без использования инструкции. –

+0

Является ли ваша вторая ошибка ('Cast') ошибкой компиляции или времени выполнения? Это будет иметь большое значение, и из вашего вопроса трудно сделать вывод. –

+0

@ Крис, да, я сделал. Хороший вопрос. – Benjamin

ответ

8

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

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>), а всего лишь базовый класс ваших объектов. В противном случае решение @Paul Keister было бы проще.

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

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

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. Общая версия проверяет это во время компиляции. Итак, если вы можете предпочитают первую версию.

+0

Я думаю, что это поможет мне, наконец, узнать дженерики! Благодарю. – Benjamin

+0

Если бы я мог дать вам всю мою репутацию здесь, я бы ... это то, что я ищу около полугода, и я всегда ошибался. Thx, ты действительно мой бог. – Zoka

0

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

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

Ваш ответ будет возвращен null, а затем попытаться получить идентификатор от него.

+0

Я думаю, что я попробовал 'Where', но у него была такая же проблема, как' FirstOrDefault'. Благодарю. – Benjamin

+0

Это устраняет проблему (неправильное использование 'FirstOrDefault'), но это не связано с вопросом. –

6

Первый конструктор не работает, потому что вы работаете с не-общим 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.

+1

и что происходит здесь, если ваш объект равен нулю, и вы пытаетесь ссылаться на id:) –

+0

Paul, Thanks. Но у меня нет 'DbSet ' на 'DbContext'. Вот почему у меня проблема? Я просто использовал 'DomainEntity' для организации других классов по наследству. Я думаю, что реальная проблема заключается в том, что я не понимаю дженерики. – Benjamin

+0

Я попытался использовать 'MakeGenericType', но он сказал, что' DbSet 'не является общим типом. – Benjamin

2

Вот такая идея, которая, похоже, работает.

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; 
} 
+1

Здесь вы можете заменить 'dynamic' на' DomainEntity', что даст вам почти то же самое, что и мой ответ. –

+3

Загрузка полной таблицы в память (возможно, 1 миллион строк), чтобы получить только один идентификатор, выглядит очень нераскрытым. Начало вашего цикла будет выполнять запрос 'set' без фильтра вообще. Держитесь подальше от этого решения. – Slauma

+0

@Slauma, кроме того, он возвращает матч после итерации всей таблицы :) post отредактирован. – Shimmy

3

Вот и проблема. Класс DbSethas its own implementation of 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; 
23

Может быть, вам не хватает

using System.Linq; 
+0

Это решило меня, как шарм! ;) – Sammy

+0

Я забыл убедиться, что System.Linq был в использовании! Это почти всегда по умолчанию, и я никогда не думал проверять его. –