Kerangka Entitas dan Tampilan SQL Server

132

Untuk beberapa alasan yang saya tidak punya kebebasan untuk berbicara tentang, kami mendefinisikan pandangan pada database Sql Server 2005 kami seperti:

CREATE VIEW [dbo].[MeterProvingStatisticsPoint]
AS
SELECT
    CAST(0 AS BIGINT) AS 'RowNumber',
    CAST(0 AS BIGINT) AS 'ProverTicketId',
    CAST(0 AS INT) AS 'ReportNumber',
    GETDATE() AS 'CompletedDateTime',
    CAST(1.1 AS float) AS 'MeterFactor',
    CAST(1.1 AS float) AS 'Density',
    CAST(1.1 AS float) AS 'FlowRate',
    CAST(1.1 AS float) AS 'Average',
    CAST(1.1 AS float) AS 'StandardDeviation',
    CAST(1.1 AS float) AS 'MeanPlus2XStandardDeviation',
    CAST(1.1 AS float) AS 'MeanMinus2XStandardDeviation'
WHERE 0 = 1

Idenya adalah bahwa Kerangka Entitas akan membuat entitas berdasarkan kueri ini, yang ia lakukan, tetapi menghasilkannya dengan kesalahan yang menyatakan sebagai berikut:

Peringatan 6002: Tabel / tampilan 'Keystone_Local.dbo.MeterProvingStatisticsPoint' tidak memiliki kunci primer yang ditentukan. Kunci telah disimpulkan dan definisi dibuat sebagai tabel / tampilan hanya-baca.

Dan itu memutuskan bahwa bidang CompletedDateTime akan menjadi kunci utama entitas ini.

Kami menggunakan EdmGen untuk menghasilkan model. Apakah ada cara untuk tidak memiliki kerangka entitas termasuk bidang pandangan ini sebagai kunci utama?

Sergio Romero
sumber

Jawaban:

245

Kami memiliki masalah yang sama dan ini solusinya:

Untuk memaksa kerangka entitas untuk menggunakan kolom sebagai kunci utama, gunakan ISNULL.

Untuk memaksa kerangka entitas agar tidak menggunakan kolom sebagai kunci utama, gunakan NULLIF.

Cara mudah untuk menerapkan ini adalah dengan membungkus pernyataan pilih tampilan Anda di pilih lain.

Contoh:

SELECT
  ISNULL(MyPrimaryID,-999) MyPrimaryID,
  NULLIF(AnotherProperty,'') AnotherProperty
  FROM ( ... ) AS temp
Tillito
sumber
2
Saya pikir ini yang terbaik untuk diharapkan. Intinya itu berfungsi.
MvcCmsJon
1
Terima kasih! Itu bekerja dengan sempurna. @ Sabanito Saya pikir itu mem-parsing definisi. itu sebabnya Anda perlu secara khusus membungkus properti kunci di IsNull (). Saya memiliki pandangan yang tidak mengembalikan nulls (dan tidak dapat mengembalikan nulls) tetapi karena cara logika ditulis, EF tidak dapat menentukan bahwa itu yang terjadi sampai saya membungkus kunci di IsNull ().
Rabbi
3
Satu-satunya masalah yang saya lihat di sini adalah bahwa tampilan mungkin secara sah perlu mengembalikan string kosong ''. Apa yang saya lakukan, hanya melemparkan kolom kembali ke tipe datanya sendiri. misalnya jika AnotherProperty memiliki tipe data varchar (50) saya akan menyebutnya sebagai 'CONVERT (VARCHAR (50), AnotherProperty) AS [AnotherProperty]'. ini menutupi nullability dari EF dan juga memungkinkan string kosong.
Bart
2
ya ini berfungsi misalnya untuk membuat EF menggunakan kolom sebagai kunci utama isnull (CONVERT (VARCHAR (50), newid ()), '') AS [PK]
dc2009
2
Selain hanya ada pesan yang mengganggu dalam solusi, apakah ada salahnya tidak memperbaiki ini? Saya setuju dengan solusi Anda, tetapi terus terang saya tidak merasa bahwa saya harus melakukan ini - saya pikir kita semua bisa setuju ini adalah bug, kan?
dyslexicanaboko
67

Saya dapat menyelesaikan ini menggunakan desainer.

  1. Buka Browser Model.
  2. Temukan tampilan dalam diagram.
  3. Klik kanan pada kunci utama, dan pastikan "Entity Key" dicentang.
  4. Pilih multi-semua kunci non-primer. Gunakan tombol Ctrl atau Shift.
  5. Di jendela Properties (tekan F4 jika perlu untuk melihatnya), ubah drop-down "Entity Key" menjadi False.
  6. Simpan perubahan.
  7. Tutup Visual Studio dan buka kembali. Saya menggunakan Visual Studio 2013 dengan EF 6 dan saya harus melakukan ini untuk menghilangkan peringatan.

Saya tidak perlu mengubah pandangan untuk menggunakan solusi ISNULL, NULLIF, atau COALESCE. Jika Anda memperbarui model Anda dari database, peringatan akan muncul kembali, tetapi akan hilang jika Anda menutup dan membuka kembali VS. Perubahan yang Anda buat pada perancang akan dipertahankan dan tidak terpengaruh oleh penyegaran.

Casey Plummer
sumber
9
Dikonfirmasi Harus me-restart VS2013 untuk membuat peringatan hilang.
Michael Logutov
5
"Sudahkah kamu mencoba mematikannya lagi?" ;-) Terima kasih, bekerja seperti pesona!
Obl Tobl
4
Ketika saya membuat tampilan, mereka bahkan tidak bisa berada di diagram model. Mereka dikomentari di file xml
ggderas
Solusi sederhana dan mudah dan tidak tampak seperti perbaikan non-hacky seperti memanipulasi tampilan! Terima kasih.
LuqJensen
2
VS2017 yang dikonfirmasikan harus dimulai kembali juga agar peringatan hilang.
Marc Levesque
46

Setuju dengan @Tillito, namun dalam kebanyakan kasus itu akan mengotori pengoptimal SQL dan tidak akan menggunakan indeks yang benar.

Mungkin terlihat jelas bagi seseorang, tetapi saya menghabiskan waktu berjam-jam untuk memecahkan masalah kinerja menggunakan solusi Tillito. Katakanlah Anda memiliki tabel:

 Create table OrderDetail
    (  
       Id int primary key,
       CustomerId int references Customer(Id),
       Amount decimal default(0)
    );
 Create index ix_customer on OrderDetail(CustomerId);

dan pandangan Anda adalah sesuatu seperti ini

 Create view CustomerView
    As
      Select 
          IsNull(CustomerId, -1) as CustomerId, -- forcing EF to use it as key
          Sum(Amount) as Amount
      From OrderDetail
      Group by CustomerId

Sql optimizer tidak akan menggunakan indeks ix_customer dan itu akan melakukan pemindaian tabel pada indeks primer, tetapi jika bukan:

Group by CustomerId

Kau gunakan

Group by IsNull(CustomerId, -1)

itu akan membuat MS SQL (setidaknya 2008) memasukkan indeks yang tepat ke dalam rencana.

Jika

Val Bakhtin
sumber
2
Ini harus menjadi komentar atas jawaban Tillito, bukan jawaban itu sendiri, karena tidak memberikan solusi untuk pertanyaan OP.
zimdanen
6
Pria itu memiliki perwakilan 1, dia belum bisa menambahkan komentar, belum.
jrcs3
@zimdanen Tidak ada cara Anda bisa memasukkan semua informasi ini ke dalam komentar, lebih masuk akal untuk memilikinya dalam jawaban yang terpisah.
Contango
2
@Contango: Jawaban ini diedit enam hari setelah diposting dan saya mengirim komentar saya. Lihat riwayat revisi.
zimdanen
9

Metode ini bekerja dengan baik untuk saya. Saya menggunakan ISNULL () untuk bidang kunci utama, dan COALESCE () jika bidang tidak boleh menjadi kunci utama, tetapi juga harus memiliki nilai yang tidak dapat dibatalkan. Contoh ini menghasilkan bidang ID dengan kunci primer yang tidak dapat dibatalkan. Bidang lainnya bukan kunci, dan memiliki (Tidak Ada) sebagai atribut Nullable mereka.

SELECT      
ISNULL(P.ID, - 1) AS ID,  
COALESCE (P.PurchaseAgent, U.[User Nickname]) AS PurchaseAgent,  
COALESCE (P.PurchaseAuthority, 0) AS PurchaseAuthority,  
COALESCE (P.AgencyCode, '') AS AgencyCode,  
COALESCE (P.UserID, U.ID) AS UserID,  
COALESCE (P.AssignPOs, 'false') AS AssignPOs,  
COALESCE (P.AuthString, '') AS AuthString,  
COALESCE (P.AssignVendors, 'false') AS AssignVendors 
FROM Users AS U  
INNER JOIN Users AS AU ON U.Login = AU.UserName  
LEFT OUTER JOIN PurchaseAgents AS P ON U.ID = P.UserID

jika Anda benar-benar tidak memiliki kunci utama, Anda bisa menipu dengan menggunakan ROW_NUMBER untuk menghasilkan kunci-pseudo yang diabaikan oleh kode Anda. Sebagai contoh:

SELECT
ROW_NUMBER() OVER(ORDER BY A,B) AS Id,
A, B
FROM SOMETABLE
SpazDude
sumber
Ya, saya akhirnya selingkuh NEWID() as id, tapi itu ide yang sama. Dan ada kasus penggunaan yang sah - misalnya jika Anda memiliki tampilan baca-saja. Jelek, EF, jelek.
ruffin
4

Generator Entity Framework EDM saat ini akan membuat kunci komposit dari semua bidang yang tidak dapat dibatalkan dalam tampilan Anda. Untuk mendapatkan kontrol atas hal ini, Anda perlu memodifikasi tampilan dan kolom tabel yang mendasari pengaturan kolom menjadi nullable ketika Anda tidak ingin mereka menjadi bagian dari kunci utama. Sebaliknya juga benar, seperti yang saya temui, kunci EDM yang dihasilkan menyebabkan masalah duplikasi data, jadi saya harus mendefinisikan kolom nullable sebagai non-nullable untuk memaksa kunci komposit dalam EDM untuk memasukkan kolom itu.

Annagram
sumber
Kami memiliki masalah yang sama dengan PK yang disimpulkan, entitas mengembalikan catatan duplikat dan benar-benar menjengkelkan. Jika Anda menjalankan Context.Entity.ToList()rekaman duplikat, tetapi jika Anda menjalankan SQL Query yang dihasilkan oleh EF secara langsung (diperoleh dengan LINQPad), tidak ada duplikasi rekaman yang terjadi. Tampaknya ada masalah memetakan catatan database ke objek entitas (POCO) yang dikembalikan, karena PK disimpulkan menggunakan logika yang dijelaskan (kolom yang tidak dapat dibatalkan).
David Oliván Ubieto
3

Sepertinya itu adalah masalah yang diketahui dengan EdmGen: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/12aaac4d-2be8-44f3-9448-d7c659585945/

RBarryYoung
sumber
Itu masuk akal. Jadi, apakah ada cara untuk mendefinisikan kolom sebagai bukan nol atau nol dalam tampilan seperti yang kita definisikan?
Sergio Romero
1
Maaf, saya sudah melampaui tingkat keahlian saya dalam Kerangka Entitas. :-)
RBarryYoung
1
Adakah yang tahu kapan masalah ini akan diperbaiki? Mengganggu harus menyelesaikan masalah ini saat Anda memiliki kolom bukan nol yang bukan kunci utama.
live-love
3

Untuk mendapatkan tampilan, saya hanya harus memperlihatkan satu kolom kunci utama. Saya membuat tampilan kedua yang menunjuk ke yang pertama dan menggunakan NULLIF untuk membuat jenis-jenisnya dapat dibatalkan. Ini berhasil bagi saya untuk membuat EF berpikir hanya ada satu kunci utama dalam tampilan.

Tidak yakin apakah ini akan membantu Anda karena saya tidak yakin EF akan menerima entitas tanpa kunci utama.

Nick Gotch
sumber
3

Jika Anda tidak ingin dipusingkan dengan apa yang seharusnya menjadi kunci utama, saya sarankan:

  1. Masukkan ROW_NUMBERke dalam pilihan Anda
  2. Atur sebagai kunci utama
  3. Atur semua kolom / anggota lainnya sebagai non-primer dalam model
Santhos
sumber
1

Karena masalah yang disebutkan di atas, saya lebih suka fungsi nilai tabel.

Jika Anda memiliki ini:

CREATE VIEW [dbo].[MyView] AS SELECT A, B FROM dbo.Something

buat ini:

CREATE FUNCTION MyFunction() RETURNS TABLE AS RETURN (SELECT * FROM [dbo].[MyView])

Maka Anda cukup mengimpor fungsi daripada tampilan.

sinar
sumber
2
Bagaimana Anda membuat asosiasi di antara entitas yang menggunakan pendekatan ini? Apa itu mungkin?
ggderas