Saya mencoba untuk menentukan bagaimana menghitung baris yang cocok pada tabel menggunakan EntityFramework.
Masalahnya adalah setiap baris mungkin memiliki banyak megabyte data (dalam bidang Biner). Tentu saja SQL akan menjadi seperti ini:
SELECT COUNT(*) FROM [MyTable] WHERE [fkID] = '1';
Saya dapat memuat semua baris dan kemudian menemukan Count dengan:
var owner = context.MyContainer.Where(t => t.ID == '1');
owner.MyTable.Load();
var count = owner.MyTable.Count();
Tapi itu sangat tidak efisien. Apakah ada cara yang lebih sederhana?
EDIT: Terima kasih, semuanya. Saya telah memindahkan DB dari lampiran pribadi sehingga saya dapat menjalankan profil; ini membantu tetapi menyebabkan kebingungan yang tidak saya duga.
Dan data real saya sedikit lebih dalam, saya akan menggunakan Truk membawa Palet dari Kasus of Items - dan saya tidak ingin Truk meninggalkan kecuali ada setidaknya satu item di dalamnya.
Upaya saya ditunjukkan di bawah ini. Bagian yang saya tidak mengerti adalah bahwa CASE_2 tidak pernah mengakses server DB (MSSQL).
var truck = context.Truck.FirstOrDefault(t => (t.ID == truckID));
if (truck == null)
return "Invalid Truck ID: " + truckID;
var dlist = from t in ve.Truck
where t.ID == truckID
select t.Driver;
if (dlist.Count() == 0)
return "No Driver for this Truck";
var plist = from t in ve.Truck where t.ID == truckID
from r in t.Pallet select r;
if (plist.Count() == 0)
return "No Pallets are in this Truck";
#if CASE_1
/// This works fine (using 'plist'):
var list1 = from r in plist
from c in r.Case
from i in c.Item
select i;
if (list1.Count() == 0)
return "No Items are in the Truck";
#endif
#if CASE_2
/// This never executes any SQL on the server.
var list2 = from r in truck.Pallet
from c in r.Case
from i in c.Item
select i;
bool ok = (list.Count() > 0);
if (!ok)
return "No Items are in the Truck";
#endif
#if CASE_3
/// Forced loading also works, as stated in the OP...
bool ok = false;
foreach (var pallet in truck.Pallet) {
pallet.Case.Load();
foreach (var kase in pallet.Case) {
kase.Item.Load();
var item = kase.Item.FirstOrDefault();
if (item != null) {
ok = true;
break;
}
}
if (ok) break;
}
if (!ok)
return "No Items are in the Truck";
#endif
Dan SQL yang dihasilkan dari CASE_1 disalurkan melalui sp_executesql , tetapi:
SELECT [Project1].[C1] AS [C1]
FROM ( SELECT cast(1 as bit) AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN (SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(cast(1 as bit)) AS [A1]
FROM [dbo].[PalletTruckMap] AS [Extent1]
INNER JOIN [dbo].[PalletCaseMap] AS [Extent2] ON [Extent1].[PalletID] = [Extent2].[PalletID]
INNER JOIN [dbo].[Item] AS [Extent3] ON [Extent2].[CaseID] = [Extent3].[CaseID]
WHERE [Extent1].[TruckID] = '....'
) AS [GroupBy1] ) AS [Project1] ON 1 = 1
[ Saya tidak benar-benar memiliki Truk, Driver, Palet, Casing, atau Item; seperti yang Anda lihat dari SQL, hubungan Truck-Pallet dan Pallet-Case adalah many-to-many - meskipun menurut saya itu tidak penting. Objek asli saya tidak berwujud dan lebih sulit untuk dijelaskan, jadi saya mengubah namanya. ]
sumber
Jawaban:
Sintaks kueri:
Sintaks metode:
Keduanya menghasilkan kueri SQL yang sama.
sumber
SelectMany()
? Apakah itu dibutuhkan? Bukankah itu akan bekerja dengan baik tanpanya?MyContainer.Where(o => o.ID == '1')
)Saya pikir Anda menginginkan sesuatu seperti
(diedit untuk mencerminkan komentar)
sumber
var count = context.MyTable.Count(t => t.MyContainer.ID == '1');
tidak lama dan jelek:var count = (from o in context.MyContainer where o.ID == '1' from t in o.MyTable select t).Count();
Tapi itu tergantung pada gaya pengkodean ...Seperti yang saya pahami, jawaban yang dipilih masih memuat semua tes terkait. Menurut blog msdn ini, ada cara yang lebih baik.
http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
Secara khusus
sumber
Find(1)
permintaan tambahan . Cukup buat entitas dan lampirkan ke konteks:var princess = new PrincessEntity{ Id = 1 }; context.Princesses.Attach(princess);
Ini kode saya:
Pastikan variabel didefinisikan sebagai IQuerizable maka ketika Anda menggunakan metode Count (), EF akan mengeksekusi sesuatu seperti
Jika tidak, jika record didefinisikan sebagai IEnumerable, sql yang dihasilkan akan menanyakan seluruh tabel dan menghitung baris yang dikembalikan.
sumber
Yah, bahkan
SELECT COUNT(*) FROM Table
akan menjadi cukup tidak efisien, terutama pada tabel besar, karena SQL Server benar-benar tidak dapat melakukan apa pun selain melakukan pemindaian tabel lengkap (pemindaian indeks berkerumun).Terkadang, cukup baik untuk mengetahui perkiraan jumlah baris dari database, dan dalam kasus seperti ini, pernyataan seperti ini mungkin cukup:
Ini akan memeriksa tampilan manajemen dinamis dan mengekstrak jumlah baris dan ukuran tabel darinya, berdasarkan tabel tertentu. Ia melakukannya dengan menjumlahkan entri untuk heap (index_id = 0) atau indeks berkerumun (index_id = 1).
Cepat, mudah digunakan, tetapi tidak dijamin 100% akurat atau mutakhir. Namun dalam banyak kasus, ini "cukup baik" (dan mengurangi beban server).
Mungkin itu akan berhasil untuk Anda juga? Tentu saja, untuk menggunakannya di EF, Anda harus membungkusnya dalam proc yang disimpan atau menggunakan panggilan langsung "Jalankan kueri SQL".
Marc
sumber
Gunakan metode ExecuteStoreQuery dari konteks entitas. Ini menghindari mengunduh seluruh kumpulan hasil dan deserialisasi menjadi objek untuk melakukan penghitungan baris sederhana.
sumber
int count = context.MyTable.Count(m => m.MyContainerID == '1')
maka SQL yang dihasilkan akan menyerupai apa yang Anda lakukan, tetapi kodenya jauh lebih bagus. Tidak ada entitas yang dimuat ke dalam memori seperti itu. Cobalah di LINQPad jika Anda suka - ini akan menunjukkan kepada Anda SQL yang digunakan di balik sampul.Saya pikir ini harus berhasil ...
sumber