404

There is an entity type called Product that is generated by entity framework. I have written this query

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

The code below throws the following error :

"The entity or complex type Shop.Product cannot be constructed in a LINQ to Entities query"

var products = productRepository.GetProducts(1).Tolist();

But when I use select p instead of select new Product { Name = p.Name}; it works correctly.

How can I preform a custom select section?

Selim Yildiz
  • 4,224
  • 6
  • 14
  • 25
Ghooti Farangi
  • 19,228
  • 14
  • 42
  • 61
  • System.NotSupportedException: 'The entity or complex type 'StudentInfoAjax.Models.Student' cannot be constructed in a LINQ to Entities query.' – Md Wahid Mar 08 '20 at 18:18

14 Answers14

407

You cannot (and should not be able to) project onto a mapped entity. You can, however, project onto an anonymous type or onto a DTO:

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

And your method will return a List of DTO's.

public List<ProductDTO> GetProducts(int categoryID)
{
    return (from p in db.Products
            where p.CategoryID == categoryID
            select new ProductDTO { Name = p.Name }).ToList();
}
Ogglas
  • 38,157
  • 20
  • 203
  • 266
Yakimych
  • 16,939
  • 7
  • 46
  • 66
  • 160
    I don't understand why I should not be able to do this... This would be very usefull... – Jonx Jul 22 '11 at 00:10
  • 120
    Well, mapped entities in EF basically represent database tables. If you project onto a mapped entity, what you basically do is **partially** load an entity, which is not a valid state. EF won't have any clue how to e.g. handle an update of such an entity in the future (the default behaviour would be probably overwriting the non-loaded fields with nulls or whatever you'll have in your object). This would be a dangerous operation, since you would risk losing some of your data in the DB, therefore it is not allowed to partially load entities (or project onto mapped entities) in EF. – Yakimych Jul 22 '11 at 08:11
  • 27
    @Yakimych that makes sense except if you have some aggregate entity that you are generating/creating via a query and therefore are fully aware/intend to create a brand new entity that you will then manipulate and later add. In this case you either have to force run the query or push into a dto and back into an entity to add - which is frustrating – Cargowire Aug 17 '11 at 17:04
  • 16
    @Cargowire - I agree, that scenario exists, and it is frustrating when you know what you are doing but are not allowed to do it due to limitations. However, had this been allowed, there would be lots of frustrated developers complaining about their data getting lost when e.g. trying to save partially loaded entities. IMO, an error that blows up with a lot of noise (throwing an exception, etc) is better than behavior which can cause hidden bugs that are difficult to track down and explain (things kind of work nicely before you start noticing missing data). – Yakimych Aug 17 '11 at 20:16
  • 14
    D.T.O - [Data Transfer Objects](http://msdn.microsoft.com/en-us/magazine/ee236638.aspx#id0080022) – tkerwood Nov 15 '11 at 06:17
  • 1
    Also, another useful scenario is very you want to load entity that you do not want to change, so you dont care about its state. For anyone that doesn't want to deal with DTO, but wants to return list of model entity, see my answer below. – Goran Sep 21 '12 at 14:14
  • Do you have to use a seperate method to get the data into a list? – Chazt3n Oct 17 '12 at 13:41
  • 1
    @Chazt3n - not sure what you're asking. Calling `ToList()` "gets the data into a List". – Yakimych Oct 18 '12 at 08:21
  • except it apparently doesn't, any tips on getting past this Timeout expired mysql error? – Chazt3n Oct 18 '12 at 13:03
  • 1
    @Chazt3n - Sure. Feel free to create a new question and explain your problem in detail. – Yakimych Oct 19 '12 at 13:46
  • 2
    This makes no sense to me.. I thought the whole point of POCO objects was for persistence ignorance and re-useablity? – Ryan Jul 14 '13 at 01:18
  • @Ryan - that's correct. What is it that doesn't make sense though? – Yakimych Jul 15 '13 at 06:48
  • 1
    @Yakimych - I don't understand why I can project using the same POCO object – Ryan Jul 15 '13 at 16:16
  • As far as this preventing one from populating a partial entity, that's true, but only for the actual query building/execution step. Nothing is stopping you from populating an entity (even with sub-entities) using a projection on the returned IEnumerable results, as obtained via something like ToList(). This is in fact rather useful at times; an untracked entity or collection of entities that don't get attached to the context. Something like var partialEntities = GetProducts(123).Select(p => new Product {Name = p.Name}); – bcr May 28 '14 at 15:31
  • This is one thing I never understood about MS Entity, I used to love Telerik's DataAccess before they switched to CodeFirst only. You could construct an object freely right in the LINQ query. It was fast and you didn't need the extra layer of BS like a DTO. – Kevin B Burns Jun 08 '16 at 18:49
  • 3
    Immediately downvoted on reading "should not be able to". This is condescending and patently wrong. EntityFramework is supposed to allow relational CRUD operations using C# idioms. An exception that says "sorry, you can't use this object in this way because it's used in a different way somewhere else" is pretty anti-idiomatic C#. – JounceCracklePop Apr 18 '18 at 19:16
  • This would be useful in a read context which uses Linq to generate data for s which would otherwise result in circular reference errors since .Net doesn't give us a great way to deal with this as far I have been able to find out. – DavidScherer May 01 '19 at 20:32
  • Using a DTO is not a solution I would recommend because usually a DTO that has no relationship to the entity. That makes using it a poor choice for solving this problem. If the code needs a type of IQueryable in order to add additional Expressions to the IQueryable later, it doesn't work to return an IQueryable. It just isn't even an option. – Rhyous Oct 26 '19 at 00:16
284

You can project into anonymous type, and then from it to model type

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 });
}

Edit: I am going to be a bit more specific since this question got a lot of attention.

You cannot project into model type directly (EF restriction), so there is no way around this. The only way is to project into anonymous type (1st iteration), and then to model type (2nd iteration).

Please also be aware that when you partially load entities in this manner, they cannot be updated, so they should remain detached, as they are.

I never did completely understand why this is not possible, and the answers on this thread do not give strong reasons against it (mostly speaking about partially loaded data). It is correct that in partially loaded state entity cannot be updated, but then, this entity would be detached, so accidental attempts to save them would not be possible.

Consider method I used above: we still have a partially loaded model entity as a result. This entity is detached.

Consider this (wish-to-exist) possible code:

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

This could also result in a list of detached entities, so we would not need to make two iterations. A compiler would be smart to see that AsNoTracking() has been used, which will result in detached entities, so it could allow us to do this. If, however, AsNoTracking() was omitted, it could throw the same exception as it is throwing now, to warn us that we need to be specific enough about the result we want.

Farzad Karimi
  • 701
  • 10
  • 25
Goran
  • 5,932
  • 3
  • 32
  • 74
  • 4
    This is the cleanest solution when you don't need / don't care about state of the selected entity you want to project. – Mário Meyrelles Aug 09 '13 at 14:44
  • 2
    And when you dont care if you return IEnumerable or IQueryable ;). But still you get my upvote cause this solution works for me now. – Michael Brennt Dec 12 '13 at 14:15
  • 11
    technically, the projection to the model type is occurring outside the query, and I believe also requires an additional iteration through the list. I won't use this solution for my code, but it is solution for the question. uptick. – 1c1cle Jan 02 '14 at 17:37
  • 1
    @1c1cle yes, if you read my first sentence, that is exactly what is says, and naturally, there are two iterations, which in most cases is not a problem. There is no such thing as projecting into a model entity directly, otherwise it would not be called projection. – Goran Feb 12 '14 at 12:11
  • 1
    Awesome answer but I can't upvote because it has 42 votes and that is a magic number – bartburkhardt May 05 '14 at 12:50
  • 1
    This script is beautiful!! – Afroman Makgalemela Jul 04 '14 at 08:28
  • 4
    I prefer this to the accepted DTO solution - much more elegant and clean – Adam Hey Sep 22 '14 at 10:22
  • 7
    Except that, with respect, it's not actually an answer to the question. This is an answer as to how to do a Linq To Objects projection, not a Linq to Entities query projection. So the DTO option is the only option re: Linq to Entities. – rism Mar 28 '15 at 05:55
  • 1
    @rism Actually, it is an answer to a question, please read it again, it is the last sentence. Linq2Entities is only mentioned as part of the exception that was raised, not as a necessity to achieve the goal. – Goran Jan 14 '16 at 14:27
  • using this solution to return a IQueryable<> I get following error when passing the result to subsequent paging method: "The source IQueryable doesn't implement IDbAsyncEnumerable. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations." Is there a workaround to this? – Luke May 22 '17 at 12:49
  • sorry but you fetch all data result in memory when you execute .ToList() method. – C.Fasolin Sep 26 '18 at 06:52
  • @C.Fasolin Nothing to be sorry about, I have already pointed that out in my first sentence, and later cleared again in comments. – Goran Sep 30 '18 at 19:56
  • This should definitely be the accepted answer. Thank you so much for such a smart way of thinking – Robouste Oct 02 '19 at 08:37
  • Calling ToList() is not a solution I would recommend because calling ToList() could be way too *un*performant as the queryable may need to be added too. For example, what if the query is to get all, and on a get all call, we force a top of 2000. Well, if you ToList() on the IQueryable you are going to query every single item from the database, which could be thousands or millions and new up objects for them, and only after that, will the additional expressions be added to the IQueryable. You want the additional expressions added before calling ToList(). – Rhyous Oct 26 '19 at 00:18
  • @Rhyous You are missing the point, adding queryable does not make difference. What mattes is that you would construct your query anyway you like, and then only when data is retrieved locally, you would do to ToList(). So, in your example, you would do ToList() on 2000 records, not millions. – Goran Oct 27 '19 at 01:52
81

There is another way that I found works, you have to build a class that derives from your Product class and use that. For instance:

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

Not sure if this is "allowed", but it works.

Tomasz Iniewicz
  • 4,151
  • 5
  • 37
  • 47
  • 3
    Clever! Tried this now and it works. I'm sure it will somehow burn me though. – Daniel May 17 '13 at 05:28
  • 5
    BTW this does bite you if you try to persist the results of GetProducts() since EF cannot find the mapping for PseudoProduct e.g. "System.InvalidOperationException: Mapping and metadata information could not be found for EntityType 'blah.PseudoProduct'". – sming Dec 24 '13 at 11:13
  • 4
    Best answer, and the only one that answers within the parameters of the question. All other answers change the return type or prematurely execute the IQueryable and use linq to objects – rdans Oct 02 '14 at 11:32
  • 2
    100% shocked it worked... in EF 6.1 this is working. – TravisWhidden Aug 05 '15 at 22:58
  • I tried this but then I got a "model backing context has changed" error. I checked update-database -script and it's trying to make this change `ALTER TABLE [dbo].[Product] ADD [Discriminator] [nvarchar](128) NOT NULL DEFAULT ''` – mejobloggs Sep 11 '15 at 08:07
  • 2
    @mejobloggs Try [NotMapped] attribute on the derived class, or .Ignore if you're using the fluent API. – Dunc Mar 31 '16 at 11:52
  • Did not work for me. I still got the same: "The entity or complex type ... cannot be constructed in a LINQ to Entities query." I was, however attempting to create an IQueryable collection to persist using context.SaveChanges(myCollection). The examples here all use .ToList(). Applying .ToList() will force eager evaluation. So that the query will not be done in one go on the server. Which defeats my purpose of fast bulk insert I want! [ using EF 6.1 ] – user1040323 Oct 01 '18 at 11:38
  • It's simple and it works :) i got an error about a missing column 'Discriminator' and I solved it using `[NotMapped]` as an attribute of the derived class – Apfelkuacha Jul 22 '19 at 21:33
  • 1
    I found this to be the best answer. I am surprised the other two answers have so many upvotes.: 1. Use Dto and 2. use anonymous followed by ToList(). I would not recommend either of those solutions. However, this one works best. First, because a child type is not the mapped type, it will not get the error. Second, a child type can act as the mapped type to apply additional expressions or queryable actions to it. – Rhyous Oct 26 '19 at 00:21
37

Here is one way to do this without declaring aditional class:

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

However, this is only to be used if you want to combine multiple entities in a single entity. The above functionality (simple product to product mapping) is done like this:

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;
}
Bojan Hrnkas
  • 1,332
  • 13
  • 21
23

Another simple way :)

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;
}
Soren
  • 5,295
  • 5
  • 36
  • 43
  • good point I just learned something IQueryable with your nice reply. It would have been nice though if you would have explained WHY it's not useful after a ToList() and the reason is that you can't use generic lists in a LINQ-to-SQL query. So if you know you're always gonna push the results into another query by the caller then certainly makes sense to be IQueryable. But if not...if you are gonna use it as a generic list after, then use the ToList() inside the method so you aren't doing a ToList() on the IQueryable each and every call to this method. – PositiveGuy Jan 26 '12 at 20:57
  • You totally alright my friend.I just imitate the question method signature, because of that I convert it to a Query-able... ;) – Soren Mar 12 '12 at 21:34
  • 1
    This works, the productList becomes uneditable after the ToList(). How can I make it editable? – doncadavona Aug 10 '15 at 02:34
  • If you put `.ToList` in query, it is executed and pulled data from server then what is the point to make it again `AsQueryable`?. – Moshii Jan 13 '17 at 23:08
  • 1
    @Moshii just in order to satisfy the method return type signature, (as I said in the answer, it's not useful anymore). – Soren Jan 14 '17 at 05:32
4

You can use this and it should be working --> You must use toList before making the new list using select:

db.Products
    .where(x=>x.CategoryID == categoryID).ToList()
    .select(x=>new Product { Name = p.Name}).ToList(); 
Draken
  • 3,049
  • 13
  • 32
  • 49
1

In response to the other question which was marked as duplicate (see here) I figured out a quick and easy solution based on the answer of Soren:

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();

Note: This solution only works if you have a navigation property (foreign key) on the Task class (here called 'Incident'). If you don't have that, you can just use one of the other posted solutions with "AsQueryable()".

Community
  • 1
  • 1
JollyBrackets
  • 471
  • 4
  • 9
1

You can solve this by using Data Transfer Objects (DTO's).

These are a bit like viewmodels where you put in the properties you need and you can map them manually in your controller or by using third-party solutions like AutoMapper.

With DTO's you can :

  • Make data serialisable (Json)
  • Get rid of circular references
  • Reduce networktraffic by leaving properties you don't need (viewmodelwise)
  • Use objectflattening

I've been learning this in school this year and it's a very useful tool.

Jelman
  • 734
  • 6
  • 17
0

If you are using Entity framework, then try removing property from DbContext which uses your complex model as Entity I had same problem when mapping multiple model into a viewmodel named Entity

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

Removing the entry from DbContext fixed my error.

0

if you are Executing Linq to Entity you can't use the ClassType with new in the select closure of query only anonymous types are allowed (new without type)

take look at this snippet of my project

//...
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

of you added the new keyword in Select closure even on the complex properties you will got this error

so remove the ClassTypes from new keyword on Linq to Entity queries ,,

because it will transformed to sql statement and executed on SqlServer

so when can I use new with types on select closure?

you can use it if you you are dealing with 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 

after I executed ToList on query it became in memory collection so we can use new ClassTypes in select

Basheer AL-MOMANI
  • 11,997
  • 8
  • 79
  • 85
  • Sure you can use anonymous types, but you cannot create an entity within the LINQ query, even to set an anonymous member, because LINQ-to-Entities still throws the same exception. – Suncat2000 Aug 28 '19 at 21:14
0

In many cases, the transformation is not needed. Think for the reason you want the strongly type List, and evaluate if you just want the data, for example, in a web service or for displaying it. It does not matter the type. You just need to know how to read it and check that is identical to the properties defined in the anonymous type that you defined. That is the optimun scenario, cause something you don't need all the fields of an entity, and that's the reason anonymous type exists.

A simple way is doing this:

IEnumerable<object> list = dataContext.Table.Select(e => new { MyRequiredField = e.MyRequiredField}).AsEnumerable();
Sterling Diaz
  • 3,370
  • 2
  • 26
  • 34
0

It won't let you map back onto Product since that is your table you are querying. You need an anonymous function, then you can add it to a ViewModel, and add each ViewModel to a List<MyViewModel> and return these. It's a slight digression, but I include caveats about handling nullable dates because these are a pain in the behind to deal with, just in case you have any. This is how I handled it.

Hopefully you have a ProductViewModel:

public class ProductViewModel
{
    [Key]
    public string ID { get; set; }
    public string Name { get; set; }
}

I have dependency injection/repository framework where I call a function to grab my data. Using your post as an example, in your Controller function call, it would look like this:

int categoryID = 1;
var prods = repository.GetProducts(categoryID);

In the repository class:

public IEnumerable<ProductViewModel> GetProducts(int categoryID)
{
   List<ProductViewModel> lstPVM = new List<ProductViewModel>();

   var anonymousObjResult = from p in db.Products
                            where p.CategoryID == categoryID 
                            select new
                            {
                                CatID = p.CategoryID,
                                Name = p.Name
                            };

        // NOTE: If you have any dates that are nullable and null, you'll need to
        // take care of that:  ClosedDate = (DateTime?)p.ClosedDate ?? DateTime.Now

        // If you want a particular date, you have to define a DateTime variable,
        // assign your value to it, then replace DateTime.Now with that variable. You
        // cannot call a DateTime.Parse there, unfortunately. 
        // Using 
        //    new Date("1","1","1800"); 
        // works, though. (I add a particular date so I can edit it out later.)

        // I do this foreach below so I can return a List<ProductViewModel>. 
        // You could do: return anonymousObjResult.ToList(); here
        // but it's not as clean and is an anonymous type instead of defined
        // by a ViewModel where you can control the individual field types

        foreach (var a in anonymousObjResult)
        {                
            ProductViewModel pvm = new ProductViewModel();
            pvm.ID = a.CatID;  
            pvm.Name = a.Name;
            lstPVM.Add(rvm);
        }

        // Obviously you will just have ONE item there, but I built it 
        // like this so you could bring back the whole table, if you wanted
        // to remove your Where clause, above.

        return lstPVM;
    }

Back in the controller, you do:

 List<ProductViewModel> lstProd = new List<ProductViewModel>();

 if (prods != null) 
 {
    // For setting the dates back to nulls, I'm looking for this value:
    // DateTime stdDate = DateTime.Parse("01/01/1800");

    foreach (var a in prods)
    {
        ProductViewModel o_prod = new ReportViewModel();
        o_prod.ID = a.ID;
        o_prod.Name = a.Name;
       // o_prod.ClosedDate = a.ClosedDate == stdDate ? null : a.ClosedDate;
        lstProd.Add(o_prod);
    }
}
return View(lstProd);  // use this in your View as:   @model IEnumerable<ProductViewModel>
vapcguy
  • 5,607
  • 1
  • 45
  • 43
-1

only add AsEnumerable() :

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products.AsEnumerable()
           where p.CategoryID== categoryID
           select new Product { Name = p.Name};
}
HamidReza
  • 1,164
  • 14
  • 14
-2

you can add AsEnumerable to your collection like the follow :

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products.AsEnumerable()
           where p.CategoryID== categoryID
           select new Product { Name = p.Name};
}
HamidReza
  • 1,164
  • 14
  • 14
  • Why this is a bad answer although it does work... .AsEnumerable ends linq to entities. The Where clause and everything else is handled outside of linq to Entities. ie every product is retrieved then filtered by linq to objects. Aside from this it is pretty much exactly the same as the .ToList answer above. https://stackoverflow.com/questions/5311034/what-is-the-effect-of-asenumerable-on-a-linq-entity – KenF Jan 30 '18 at 23:18
  • 1
    The problem with this is that is just a select * from... performed, not select new Product { Name = p.Name}, since you will get also a cyclic reference. And you want just the Name. – Sterling Diaz Mar 04 '18 at 04:27