Cant использует выражение linq, объект или сложный тип '' не может быть сконструирован в запросе LINQ to Entities [duplicate]

Я делаю этот метод для глубокого присваивания с помощью es6.

function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item) && item !== null)
}

function deepAssign(...objs) {
    if (objs.length < 2) {
        throw new Error('Need two or more objects to merge')
    }

    const target = objs[0]
    for (let i = 1; i < objs.length; i++) {
        const source = objs[i]
        Object.keys(source).forEach(prop => {
            const value = source[prop]
            if (isObject(value)) {
                if (target.hasOwnProperty(prop) && isObject(target[prop])) {
                    target[prop] = deepAssign(target[prop], value)
                } else {
                    target[prop] = value
                }
            } else if (Array.isArray(value)) {
                if (target.hasOwnProperty(prop) && Array.isArray(target[prop])) {
                    const targetArray = target[prop]
                    value.forEach((sourceItem, itemIndex) => {
                        if (itemIndex < targetArray.length) {
                            const targetItem = targetArray[itemIndex]

                            if (Object.is(targetItem, sourceItem)) {
                                return
                            }

                            if (isObject(targetItem) && isObject(sourceItem)) {
                                targetArray[itemIndex] = deepAssign(targetItem, sourceItem)
                            } else if (Array.isArray(targetItem) && Array.isArray(sourceItem)) {
                                targetArray[itemIndex] = deepAssign(targetItem, sourceItem)
                            } else {
                                targetArray[itemIndex] = sourceItem
                            }
                        } else {
                            targetArray.push(sourceItem)
                        }
                    })
                } else {
                    target[prop] = value
                }
            } else {
                target[prop] = value
            }
        })
    }

    return target
}
336
задан Gilad Green 10 October 2016 в 18:56
поделиться

13 ответов

Вы не можете (и не должны) проектировать на сопоставленную сущность. Вы можете, однако, проецировать на анонимный тип или на DTO :

public class ProductDTO
{
    public string Name { get; set; }
    // Other field you may need from the Product entity
}

И ваш метод вернет список DTO.

public List<ProductDTO> GetProducts(int categoryID)
{
    return (from p in db.Products
            where p.CategoryID == categoryID
            select new ProductDTO { Name = p.Name }).ToList();
}
331
ответ дан jm. 19 August 2018 в 06:50
поделиться
  • 1
    Я не понимаю, почему я не мог этого сделать ... Это было бы очень полезно ... – Jonx 22 July 2011 в 01:10
  • 2
    Ну, сопоставленные объекты в EF в основном представляют таблицы базы данных. Если вы проецируете на сопоставленную сущность, то, что вы в основном делаете, это частично загружает объект, который не является допустимым состоянием. EF не будет знать, как, например, обработайте обновление такого объекта в будущем (поведение по умолчанию, вероятно, приведет к перезаписи незагруженных полей с нулями или любым другим, что у вас будет в вашем объекте). Это будет опасной операцией, поскольку вы рискуете потерять некоторые данные в БД, поэтому не разрешается частично загружать объекты (или проектировать на сопоставленные объекты) в EF. – Yakimych 22 July 2011 в 09:11
  • 3
    @Yakimych имеет смысл, за исключением случаев, когда у вас есть какая-то совокупная сущность, которую вы генерируете / создаете с помощью запроса, и поэтому полностью осознаете / намерены создать совершенно новую сущность, которую затем будете манипулировать, а затем добавлять. В этом случае вам нужно либо принудительно запустить запрос, либо нажать в dto и обратно в объект, который нужно добавить - что расстраивает – Cargowire 17 August 2011 в 18:04
  • 4
    @Cargowire - Я согласен, этот сценарий существует, и это разочаровывает, когда вы знаете, что делаете, но им не разрешено делать это из-за ограничений. Однако, если бы это было разрешено, было бы много разочарованных разработчиков, жалующихся на то, что их данные теряются, когда, например, пытаясь сохранить частично загруженные объекты. IMO, ошибка, которая взрывается большим количеством шума (выбрасывание исключения и т. Д.), Лучше, чем поведение, которое может вызвать скрытые ошибки, которые трудно отследить и объяснить (что-то вроде работы, прежде чем вы начнете замечать отсутствующие данные). – Yakimych 17 August 2011 в 21:16
  • 5

, если вы выполняете Linq to Entity, вы не можете использовать ClassType с new в закрытии select запроса only anonymous types are allowed (new without type)

взглянуть на этот фрагмент моего проекта

//...
var dbQuery = context.Set<Letter>()
                .Include(letter => letter.LetterStatus)
                .Select(l => new {Title =l.Title,ID = l.ID, LastModificationDate = l.LastModificationDate, DateCreated = l.DateCreated,LetterStatus = new {ID = l.LetterStatusID.Value,NameInArabic = l.LetterStatus.NameInArabic,NameInEnglish = l.LetterStatus.NameInEnglish} })
                               ^^ without type__________________________________________________________________________________________________________^^ without type

из вас добавил new keyword в закрытии Select даже в complex properties, вы получите эту ошибку

, поэтому remove ключевое слово ClassTypes from new Linq to Entity queries ,,

, потому что он преобразуется в sql-оператор и выполняется на SqlServer

, поэтому, когда я могу использовать new with types на закрытии select?

вы можете использовать его, если вы имеете дело с LINQ to Object (in memory collection)

//opecations in tempList , LINQ to Entities; so we can not use class types in select only anonymous types are allowed
var tempList = dbQuery.Skip(10).Take(10).ToList();// this is list of <anonymous type> so we have to convert it so list of <letter>

//opecations in list , LINQ to Object; so we can use class types in select
list = tempList.Select(l => new Letter{ Title = l.Title, ID = l.ID, LastModificationDate = l.LastModificationDate, DateCreated = l.DateCreated, LetterStatus = new LetterStatus{ ID = l.LetterStatus.ID, NameInArabic = l.LetterStatus.NameInArabic, NameInEnglish = l.LetterStatus.NameInEnglish } }).ToList();
                                ^^^^^^ with type 

после того, как я выполнил ToList по запросу, он стал in memory collection, поэтому мы можем использовать new ClassTypes в выберите

0
ответ дан Basheer AL-MOMANI 19 August 2018 в 06:50
поделиться

Вот один из способов сделать это, не объявляя класс aditional:

public List<Product> GetProducts(int categoryID)
{
    var query = from p in db.Products
            where p.CategoryID == categoryID
            select new { Name = p.Name };
    var products = query.ToList().Select(r => new Product
    {
        Name = r.Name;
    }).ToList();

    return products;
}

Однако это нужно использовать, только если вы хотите объединить несколько объектов в одном объекте. Вышеупомянутая функциональность (простое сопоставление продукта с продуктом) выполняется следующим образом:

public List<Product> GetProducts(int categoryID)
{
    var query = from p in db.Products
            where p.CategoryID == categoryID
            select p;
    var products = query.ToList();

    return products;
}
32
ответ дан Bojan Hrnkas 19 August 2018 в 06:50
поделиться

В ответ на другой вопрос, который был отмечен как дубликат ( см. здесь ), я вычислил быстрое и простое решение, основанное на ответе Сорена:

data.Tasks.AddRange(
    data.Task.AsEnumerable().Select(t => new Task{
        creator_id   = t.ID,
        start_date   = t.Incident.DateOpened,
        end_date     = t.Incident.DateCLosed,
        product_code = t.Incident.ProductCode
        // so on...
    })
);
data.SaveChanges();

Примечание. Это решение работает только в том случае, если у вас есть свойство навигации (внешний ключ) в классе Task (здесь называется «Инцидент»). Если у вас этого нет, вы можете использовать одно из других опубликованных решений с помощью «AsQueryable ()».

1
ответ дан Community 19 August 2018 в 06:50
поделиться

Вы можете использовать это, и он должен работать.> Вы должны использовать toList, прежде чем создавать новый список, используя select:

db.Products
    .where(x=>x.CategoryID == categoryID).ToList()
    .select(x=>new Product { Name = p.Name}).ToList(); 
3
ответ дан Draken 19 August 2018 в 06:50
поделиться
  • 1
    Тем не менее, это все равно будет «SELECT * FROM [..]», а не «SELECT name FROM [..]» – Timo Hermans 2 November 2017 в 10:09

Вы можете проецировать в анонимный тип, а затем от него к типу модели

public IEnumerable<Product> GetProducts(int categoryID)
{
    return (from p in Context.Set<Product>()
            where p.CategoryID == categoryID
            select new { Name = p.Name }).ToList()
           .Select(x => new Product { Name = x.Name });
}

Редактировать: я собираюсь быть более конкретным, так как этот вопрос получил большое внимание.

Вы не можете напрямую проецироваться в тип модели (ограничение EF), поэтому нет никакого способа обойти это. Единственный способ - проецировать на анонимный тип (1-я итерация), а затем на тип модели (2-я итерация).

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

Я никогда не понимал, почему это невозможно, и ответы на эту тему не дают против этого серьезных оснований (в основном, о частично загруженных данных). Правильно, что в частично загруженном состоянии сущность не может быть обновлена, но тогда этот объект будет отсоединен, поэтому случайные попытки сохранить их будут невозможны.

Рассмотрим метод, который я использовал выше: у нас все еще есть в результате получается частично загруженная модель.

Рассмотрим этот возможный код:

return (from p in Context.Set<Product>()
        where p.CategoryID == categoryID
        select new Product { Name = p.Name }).AsNoTracking().ToList();

Это также может привести к списку отдельных объектов, поэтому нам не понадобится сделать две итерации. Компилятор был бы умен, чтобы увидеть, что AsNoTracking () используется, что приведет к отсоединенным объектам, чтобы оно могло позволить нам это сделать. Если, однако, AsNoTracking () был опущен, он мог бы сделать то же исключение, что и сейчас, чтобы предупредить нас, что нам нужно быть достаточно конкретным относительно желаемого результата.

235
ответ дан eFarzad 19 August 2018 в 06:50
поделиться
  • 1
    Это самое чистое решение, когда вам не нужно / не заботится о состоянии выбранного объекта, который вы хотите проектировать. – Mário Meyrelles 9 August 2013 в 15:44
  • 2
    И когда вам не нравится, если вы вернете IEnumerable или IQueryable;). Но все-таки вы получаете мое преимущество, потому что это решение работает для меня сейчас. – Michael Brennt 12 December 2013 в 16:15
  • 3
    технически проекция на тип модели происходит вне запроса, и я считаю, что также требуется дополнительная итерация через список. Я не буду использовать это решение для своего кода, но это решение для вопроса. Всплеск. – 1c1cle 2 January 2014 в 19:37
  • 4
    Я предпочитаю это для принятого решения DTO - гораздо более элегантного и чистого – Adam Hey 22 September 2014 в 11:22
  • 5
    Кроме того, с уважением, на самом деле это не ответ на вопрос. Это ответ о том, как сделать проект Linq To Objects, а не проекцию запроса Linq to Entities. Таким образом, опция DTO является единственным вариантом re: Linq to Entities. – rism 28 March 2015 в 06:55

вы можете добавить AsEnumerable в свою коллекцию, как показано ниже:

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products.AsEnumerable()
           where p.CategoryID== categoryID
           select new Product { Name = p.Name};
}
3
ответ дан HamidReza 19 August 2018 в 06:50
поделиться
  • 1
    НИКОГДА не делай этого! Это приведет к извлечению всех данных из БД, а затем сделает выбор. – Gh61 11 September 2017 в 14:39
  • 2
    Вот почему в некоторых компаниях Linq запрещено использовать. – hakan 19 November 2017 в 19:13
  • 3
    Почему это плохой ответ, хотя он работает ... .AsEnumerable заканчивает linq для сущностей. Предложение Where и все остальное обрабатываются за пределами linq для Entities. т.е. каждый продукт извлекается, а затем фильтруется linq на объекты. Помимо этого, это почти точно так же, как и ответ .ToList выше. [Д0] stackoverflow.com/questions/5311034/… – KenF 31 January 2018 в 00:18
  • 4
    Проблема в том, что это просто select * from ... done, а не select new Product {Name = p.Name}, так как вы получите также циклическую ссылку. И вы хотите просто Имя. – Sterling Diaz 4 March 2018 в 05:27

Вы можете решить эту проблему с помощью объектов передачи данных (DTO).

Это немного похоже на режимы просмотра, в которые вы помещаете нужные вам свойства, и можете их вручную сопоставить в своем контроллере или с помощью третьего -партийные решения, такие как AutoMapper.

С помощью DTO вы можете:

  • Сделать сериализуемые данные (Json)
  • Избавиться от круговых ссылок
  • Уменьшить networktraffic, оставив свойства, которые вам не нужны (viewmodelwise)
  • Использовать objectflattening

Я изучал это в школе в этом году, и это очень полезный инструмент.

0
ответ дан Jelman 19 August 2018 в 06:50
поделиться

Еще один простой способ:)

public IQueryable<Product> GetProducts(int categoryID)
{
    var productList = db.Products
        .Where(p => p.CategoryID == categoryID)
        .Select(item => 
            new Product
            {
                Name = item.Name
            })
        .ToList()
        .AsQueryable(); // actually it's not useful after "ToList()" :D

    return productList;
}
21
ответ дан Soren 19 August 2018 в 06:50
поделиться
  • 1
    Хороший момент Я только что узнал что-то IQueryable с вашим хорошим ответом. Было бы неплохо, если бы вы объяснили, почему это не полезно после ToList (), и причина в том, что вы не можете использовать общие списки в запросе LINQ-to-SQL. Поэтому, если вы знаете, что вы всегда будете подталкивать результаты к другому запросу вызывающего, тогда, безусловно, имеет смысл быть IQueryable. Но если нет ... если вы собираетесь использовать его в качестве общего списка после, то используйте метод ToList () внутри метода, чтобы вы не делали ToList () в IQueryable каждый вызов этого метода. – PositiveGuy 26 January 2012 в 22:57
  • 2
    Ты полностью в порядке мой друг. Я просто подражаю методу подписи вопроса, из-за этого я конвертирую его в Query-able ...;) – Soren 12 March 2012 в 23:34
  • 3
    Это работает, productList становится uneditable после ToList (). Как я могу сделать его редактируемым? – doncadavona 10 August 2015 в 02:34
  • 4
    Если вы поместите .ToList в запрос, он будет выполнен и вытащил данные с сервера, то в чем смысл сделать его снова AsQueryable ?. – Moshii 14 January 2017 в 00:08
  • 5
    @Moshii только для того, чтобы удовлетворить методу возврата типа метода (как я уже сказал в ответе, он больше не полезен). – Soren 14 January 2017 в 06:32

Во многих случаях преобразование не требуется. Подумайте по причине, по которой вы хотите строго напечатать List, и оцените, хотите ли вы просто данные, например, в веб-службе или для ее отображения. Это не имеет значения. Вам просто нужно знать, как его читать, и проверить, что это идентично свойствам, определенным в анонимном типе, который вы определили. Это сценарий optimun, вызывающий то, что вам не нужны все поля сущности, и это причина анонимного типа.

Простой способ делает это:

IEnumerable<object> list = dataContext.Table.Select(e => new { MyRequiredField = e.MyRequiredField}).AsEnumerable();
0
ответ дан Sterling Diaz 19 August 2018 в 06:50
поделиться

Есть еще один способ, которым я нашел работу, вам нужно построить класс, который происходит из вашего класса Product и использовать его. Например:

public class PseudoProduct : Product { }

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products
           where p.CategoryID== categoryID
           select new PseudoProduct() { Name = p.Name};
}

Не уверен, что это разрешено, но оно работает.

66
ответ дан Tomasz Iniewicz 19 August 2018 в 06:50
поделиться
  • 1
    Умная! Пробовал это сейчас, и он работает. Я уверен, что это как-то сожжет меня. – Daniel 17 May 2013 в 06:28
  • 2
    Кстати, это укусит вас, если вы попытаетесь сохранить результаты GetProducts (), поскольку EF не может найти отображение для PseudoProduct, например. "System.InvalidOperationException: информация о сопоставлении и метаданных не может быть найдена для EntityType 'blah.PseudoProduct' & quot ;. – sming 24 December 2013 в 13:13
  • 3
    Так идеально, что я добавил это в файл генерации кода .tt – AndyClaw 22 January 2014 в 04:16
  • 4
    Кажется, не работает в последней EF. – Andrew Savinykh 4 August 2014 в 10:31
  • 5
    Лучший ответ, и единственный, который отвечает в пределах параметров вопроса. Все остальные ответы изменяют тип возвращаемого значения или преждевременно выполняют IQueryable и используют linq для объектов – rdans 2 October 2014 в 12:32

Если вы используете фреймворк Entity, попробуйте удалить свойство из DbContext, который использует вашу сложную модель как Entity. У меня была такая же проблема при сопоставлении нескольких моделей в режиме просмотра с именем Entity

public DbSet<Entity> Entities { get; set; }

Удаление записи из DbContext исправил мою ошибку.

0
ответ дан vikas suhag 19 August 2018 в 06:50
поделиться
2
ответ дан HamidReza 30 October 2018 в 18:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: