エンティティフレームワークによって生成されるproductというエンティティタイプがあります。私はこの質問を書いた
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
以下のコードは以下のエラーを投げます。
"エンティティ型または複合型Shop.Productは、LINQ to Entitiesクエリでは構成できません。"
var products = productRepository.GetProducts(1).Tolist();
しかし、私がselect p
の代わりにselect new Product { Name = p.Name};
を使うとき、それは正しく働きます。
カスタム選択セクションを作成する方法
マッピングされたエンティティに投影することはできません(できないはずです)。ただし、匿名型または 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();
}
匿名型に射影し、それからモデル型に射影できます。
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();
これは分離されたエンティティのリストにもなるので、2回繰り返す必要はありません。 AsNoTracking()が使用されていることを確認するのは賢いコンパイラです。その結果、エンティティが分離されるため、これを実行できる可能性があります。しかし、AsNoTracking()が省略された場合、目的の結果について十分に具体的である必要があることを警告するために、現在スローされているのと同じ例外がスローされる可能性があります。
私は作品を見つけた別の方法があります、あなたはあなたの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};
}
これが「許可」されているかどうかはわかりませんが、機能します。
これは、追加クラスを宣言せずにこれを行う1つの方法です。
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;
}
もう一つの簡単な方法:)
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;
}
これを使えばうまくいくはずです - > selectを使って新しいリストを作る前にtoList
を使わなければなりません。
db.Products
.where(x=>x.CategoryID == categoryID).ToList()
.select(x=>new Product { Name = p.Name}).ToList();
これを解決するには、データ転送オブジェクト(DTO)を使用します。
これらは、必要なプロパティを設定するビューモデルに少し似ており、コントローラで手動で、またはAutoMapperなどのサードパーティ製のソリューションを使用してそれらをマッピングできます。
DTOを使えば、次のことができます。
私は今年学校でこれを学びました、そしてそれは非常に役に立つ道具です。
重複しているとマークされた他の質問( はこちら を参照)に答えて、私は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();
注:この解決策は、Taskクラスにナビゲーションプロパティ(外部キー)がある場合にのみ機能します(ここでは 'Incident'と呼びます)。それがない場合は、 "AsQueryable()"で他の投稿されたソリューションの1つを使用することができます。
asEnumerable()を追加するだけです。
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products.AsEnumerable()
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
Linq to Entity
を実行している場合は、クエリonly anonymous types are allowed (new without type)
のClassType
クロージャーでnew
と共にselect
を使用することはできません。
私のプロジェクトのこの断片を見てください。
//...
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
を追加したので、このエラーが発生します
ClassTypes from new
クエリのLinq to Entity
キーワードはremove
です。
それはsqlステートメントに変換されSqlServer上で実行されるからです。
だからいつselect
クロージャーでnew with types
を使うことができますか?
あなたが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
になりましたので、selectでnew ClassTypes
を使用できます。
Entityフレームワークを使用している場合は、複雑なモデルをEntityとして使用するDbContextからプロパティを削除してみてください。複数のモデルをEntityという名前のビューモデルにマッピングするときも同じ問題がありました
public DbSet<Entity> Entities { get; set; }
DbContextからエントリを削除するとエラーが修正されました。
多くの場合、変換は不要です。リストを強く入力する必要があるという理由で考え、データが単に欲しいのかどうか、例えばWebサービスやそれを表示するために評価します。種類は関係ありません。あなたはそれを読む方法を知っていて、それがあなたが定義した匿名型で定義されたプロパティと同一であることを確認する必要があります。それがoptimunのシナリオであり、エンティティのすべてのフィールドを必要としない何かの原因となります。それが匿名型が存在する理由です。
簡単な方法はこれを行うことです。
IEnumerable<object> list = dataContext.Table.Select(e => new { MyRequiredField = e.MyRequiredField}).AsEnumerable();