Entity Framework: bảng không có khóa chính


165

Tôi có một DB hiện có mà tôi muốn xây dựng một ứng dụng mới bằng cách sử dụng EF4.0

Một số bảng không có khóa chính được xác định để khi tôi tạo Mô hình dữ liệu thực thể mới, tôi nhận được thông báo sau:

The table/view TABLE_NAME does not have a primary key defined 
and no valid primary key could be inferred. This table/view has 
been excluded. To use the entity, you will need to review your schema, 
add the correct keys, and uncomment it.

Nếu tôi muốn sử dụng chúng và sửa đổi dữ liệu, tôi nhất thiết phải thêm PK vào các bảng đó hay có cách giải quyết nào để tôi không phải làm vậy không?


21
Để trích dẫn Joe Celko: nếu nó không có khóa chính thì đó không phải là bảng . Tại sao mọi người lại tạo một bảng "thông thường" mà không có khóa chính ?? Chỉ cần thêm PK! Bạn sẽ cần chúng - thay vì sớm hơn ....
marc_s

4
Nếu đó là một góc nhìn này, hãy xem trường hợp này stackoverflow.com/a/10302066/413032
Davut Gürbüz

50
Hoàn toàn hợp lệ khi không phải bảng nào cũng cần khóa chính. Không thường xuyên hữu ích, nhưng hợp lệ. Khó hiểu EF là một lý do tốt, không phải là nó mất nhiều. ;-).
Suncat2000

25
Hãy tưởng tượng rằng tôi không thể sửa đổi cấu trúc DB trên công ty của mình và nó được tạo bởi ai đó sẽ không thay đổi cấu trúc bảng, kịch bản này là có thể.
Tito

4
Đây chính xác là nơi chúng tôi đang ở. Chúng tôi phải làm việc với cơ sở dữ liệu Oracle của bên thứ 3 không có khóa chính.
David Brower

Câu trả lời:


59

Lỗi có nghĩa chính xác những gì nó nói.

Ngay cả khi bạn có thể giải quyết vấn đề này, hãy tin tôi, bạn cũng không muốn. Số lượng lỗi khó hiểu có thể được giới thiệu là đáng kinh ngạc và đáng sợ, chưa kể thực tế là hiệu suất của bạn có thể sẽ đi xuống các ống.

Đừng làm việc xung quanh việc này. Sửa mô hình dữ liệu của bạn.

EDIT: Tôi đã thấy rằng một số người đang bỏ qua câu hỏi này. Điều đó tốt, tôi cho rằng, nhưng hãy nhớ rằng OP đã hỏi về việc ánh xạ một bảng mà không có khóa chính, không phải là một khung nhìn . Câu trả lời vẫn vậy. Làm việc xung quanh nhu cầu của PK để có PK trên các bảng là một ý tưởng tồi từ quan điểm về khả năng quản lý, tính toàn vẹn dữ liệu và hiệu suất.

Một số người đã nhận xét rằng họ không có khả năng sửa mô hình dữ liệu cơ bản vì họ đang ánh xạ tới ứng dụng của bên thứ ba. Đó không phải là một ý tưởng tốt, vì mô hình có thể thay đổi từ bên dưới bạn. Có thể cho rằng, trong trường hợp đó, bạn sẽ muốn ánh xạ tới một khung nhìn, một lần nữa, đó không phải là những gì OP yêu cầu.


44
Đồng ý trong các kịch bản chung nhưng trong các kịch bản hiếm như bảng LOG, bạn chỉ cần chèn các bản ghi càng sớm càng tốt. Có PK có thể là một vấn đề khi kiểm tra tính duy nhất và lập chỉ mục xảy ra. Ngoài ra Nếu PK của bạn là SẮC nên việc trả lại giá trị được tạo cho EF là một vấn đề khác. sử dụng GUID thay thế? thời gian tạo và lập chỉ mục / sắp xếp là một vấn đề khác! ... SO IN một số kịch bản OLTP quan trọng (như Ghi nhật ký) không có PK là một điểm và không có điểm tích cực nào!
Mahmoud Moravej

5
@MahmoudMoravej: Trước hết, đừng trộn lẫn các ý tưởng phân cụm chỉ mục và khóa chính. Chúng không giống nhau. Bạn có thể có các phần chèn hiệu suất rất cao trên các bảng với các chỉ báo được nhóm trên các cột IDENTITY. Nếu bạn gặp vấn đề với bảo trì chỉ mục, bạn nên phân vùng bảng đúng cách. Rời khỏi một bảng không có chỉ mục được nhóm cũng có nghĩa là bạn không thể chống phân mảnh một cách hiệu quả để lấy lại không gian sau khi xóa. Tôi thương hại người nghèo cố gắng truy vấn bảng ghi nhật ký của bạn nếu nó không có chỉ mục.
Dave Markle

149
"Sửa mô hình dữ liệu của bạn" không phải là một câu trả lời thực sự. Đôi khi chúng ta phải sống với những tình huống không lý tưởng mà chúng ta không tạo ra và không thể thay đổi. Và, như @Colin đã nói, có một cách để làm chính xác những gì OP đang yêu cầu.
TheSmurf

13
Thay đổi mã để đáp ứng EF là một cách giải quyết. Không phải tất cả các bảng đều cần một khóa chính và chúng cũng không bị bắt buộc. Ví dụ: bạn có một Chủ đề và nó có 0 hoặc nhiều từ khóa. Bảng từ khóa có thể có id chủ đề gốc và từ khóa tương ứng. Để nói rằng tôi cần phải sửa sang lại DB vì tôi buộc tôi phải khập khiễng.
Ông trùm

14
Điều này nên được đánh giá thấp bởi vì nó không trả lời câu hỏi. Chúng tôi thường cần phải làm việc với cơ sở dữ liệu của bên thứ ba không thể thay đổi.
Kurren

104

Tôi nghĩ điều này được giải quyết bởi Tillito:

Khung thực thể và SQL Server View

Tôi sẽ trích dẫn mục của anh ấy dưới đây:

Chúng tôi đã có cùng một vấn đề và đây là giải pháp:

Để buộc khung thực thể sử dụng cột làm khóa chính, hãy sử dụng ISNULL.

Để buộc khung thực thể không sử dụng cột làm khóa chính, hãy sử dụng NULLIF.

Một cách dễ dàng để áp dụng điều này là bọc câu lệnh chọn của chế độ xem của bạn trong vùng chọn khác.

Thí dụ:

SELECT
  ISNULL(MyPrimaryID,-999) MyPrimaryID,
  NULLIF(AnotherProperty,'') AnotherProperty
  FROM ( ... ) AS temp

đã trả lời 26 tháng 4, 10 lúc 17:00 bởi Tillito


6
+1 Đây là câu trả lời đúng, trong một thế giới hoàn hảo, sẽ rất tuyệt nếu đi vào và sửa đổi tất cả các cơ sở dữ liệu kế thừa để có tính toàn vẹn tham chiếu, nhưng thực tế không phải lúc nào cũng có thể.
Dylan Hayes

9
Tôi sẽ không đề nghị điều này. Đặc biệt là phần ISNULL. Nếu EF phát hiện hai PK giống nhau, nó có thể không hiển thị (các) bản ghi duy nhất và thay vào đó trả về một đối tượng được chia sẻ. Điều này đã xảy ra với tôi trước đó.
Todd

@Todd - làm sao điều đó có thể xảy ra nếu MyPrimaryID là cột KHÔNG NULL?
JoeCool

@JoeCool, chỉ vì nó KHÔNG NULL không có nghĩa là nó là duy nhất. Tôi đã nâng cấp "CÔNG TRÌNH GIẢI PHÁP NÀY ...", bởi vì dù nó được sử dụng trong bối cảnh nào, bạn có thể yên tâm về tính độc đáo. Mặc dù bây giờ nghĩ về nó, nếu một bản ghi bị xóa sẽ thay đổi hiệu quả các bản ghi "PK" sau đây.
Todd

1
-1, vì điều này không trả lời cách định cấu hình mã Entity Framework / C # để đối phó với cách ánh xạ tới bảng thiếu hạt giống nhận dạng. Một số phần mềm của bên thứ 3 (vì một số lý do) được viết theo cách này.
Andrew Gray

27

Nếu tôi muốn sử dụng chúng và sửa đổi dữ liệu, tôi nhất thiết phải thêm PK vào các bảng đó hay có cách giải quyết nào để tôi không phải làm vậy không?

Đối với những người tiếp cận câu hỏi này và đang sử dụng Entity Framework Core, bạn không còn cần thiết phải thêm PK để xóa bảng hoặc thực hiện bất kỳ cách giải quyết nào. Kể từ EF Core 2.1, chúng tôi có một loại Truy vấn tính năng mới

Các loại truy vấn phải được sử dụng cho:

  • Phục vụ như kiểu trả về cho các truy vấn ad hoc FromSql ().
  • Ánh xạ tới các khung nhìn cơ sở dữ liệu.
  • Ánh xạ tới các bảng không có khóa chính được xác định.
  • Ánh xạ tới các truy vấn được xác định trong mô hình.

Vì vậy, trong DbContext của bạn chỉ cần thêm thuộc tính sau của loại DbQuery<T>thay vì DbSet<T>như dưới đây. Giả sử tên bảng của bạn là MyTable:

public DbQuery<MyTable> MyTables { get; set; }

1
Câu trả lời tốt nhất nếu bạn đang sử dụng EF Core!
Jay

17

Các khóa tổng hợp cũng có thể được thực hiện với Entity Framework Fluent API

public class MyModelConfiguration : EntityTypeConfiguration<MyModel>
{
     public MyModelConfiguration()
     {
        ToTable("MY_MODEL_TABLE");
        HasKey(x => new { x.SourceId, x.StartDate, x.EndDate, x.GmsDate });
        ...
     }
}

Trong trường hợp 'ánh xạ thủ công' này, tôi thấy rằng việc chỉ định khóa tùy chỉnh khi bạn hiển thị là hiệu quả; ngoài ra, nếu bạn không có lợi ích của khóa tổng hợp (như trong câu trả lời này), bạn có thể gắn thẻ modelBuilder.Entity<T>()cuộc gọi theo chuỗi .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)cho những khóa quá đặc biệt không tự nhiên hoặc hỗn hợp, nhưng có thể được dựa vào trở thành duy nhất (thông thường, dù sao đi nữa.)
Andrew Gray

5

Trong trường hợp của tôi, tôi phải ánh xạ một thực thể vào Chế độ xem, không có khóa chính. Hơn nữa, tôi không được phép sửa đổi Chế độ xem này. May mắn thay, Chế độ xem này có một cột là một chuỗi duy nhất. Giải pháp của tôi là đánh dấu cột này làm khóa chính:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[StringLength(255)]
public string UserSID { get; set; }

Lừa dối EF. Hoạt động hoàn hảo, không ai để ý ... :)


không .. Nó sẽ tạo UserIDcột nếu bạn sử dụng phương pháp Code First ..!
Deepak Sharma

1
Bạn đã không lừa dối EF. Bạn vừa ra lệnh tắt Itentitychức năng. Về cơ bản, nếu tôi đúng, nó vẫn sẽ tạo UserIDcột cho bạn dưới dạng PK nhưng nó sẽ không tự động tăng UserIDkhi bạn tạo một bản ghi mới như mặc định. Ngoài ra, bạn vẫn cần giữ các giá trị riêng biệt trong UserID.
Celdor

1
@Celdor UserSID là một chuỗi, nó sẽ không bao giờ được "tăng tự động". Nếu đó là một cột nhận dạng số nguyên thì cơ sở dữ liệu sẽ tăng nó khi chèn, không phải Entity Framework.
reggaeg Ức

4

EF không yêu cầu khóa chính trên cơ sở dữ liệu. Nếu có, bạn không thể liên kết các thực thể với các khung nhìn.

Bạn có thể sửa đổi SSDL (và cơ sở dữ liệu) để chỉ định một trường duy nhất làm khóa chính của bạn. Nếu bạn không có một lĩnh vực độc đáo, thì tôi tin rằng bạn đang bị lừa. Nhưng bạn thực sự nên có một trường duy nhất (và PK), nếu không bạn sẽ gặp vấn đề sau này.

Erick


Điều này tránh được vụ hack ISNULL. Nhưng tùy thuộc vào tình huống, các câu trả lời khác có thể được yêu cầu - tôi có cảm giác rằng một số loại dữ liệu không được hỗ trợ cho PK trong EF chẳng hạn.
Todd

3

Có một khóa nhận dạng vô dụng đôi khi là vô nghĩa. Tôi tìm thấy nếu ID không được sử dụng, tại sao thêm nó? Tuy nhiên, Entity không tha thứ cho nó, vì vậy thêm trường ID sẽ là tốt nhất. Ngay cả trong trường hợp nó không được sử dụng, vẫn tốt hơn là xử lý các lỗi không liên tục của Entity về khóa nhận dạng bị thiếu.


3

CÔNG TRÌNH GIẢI PHÁP NÀY

Bạn không cần lập bản đồ thủ công ngay cả khi bạn không có PK. Bạn chỉ cần nói với EF rằng một trong các cột của bạn là cột chỉ mục và cột chỉ mục là không thể rỗng.

Để thực hiện việc này, bạn có thể thêm số hàng vào chế độ xem của mình bằng hàm isNull như sau

select 
    ISNULL(ROW_NUMBER() OVER (ORDER BY xxx), - 9999) AS id
from a

ISNULL(id, number) là điểm chính ở đây vì nó cho EF biết rằng cột này có thể là khóa chính


2
Tôi sẽ không đề xuất phần ISNULL mặc dù. Nếu EF phát hiện hai PK giống nhau, nó có thể không hiển thị bản ghi duy nhất và thay vào đó trả về một đối tượng được chia sẻ. Điều này đã xảy ra với tôi trước đó.
Todd

1
Bạn phải sử dụng isnull, nếu không thì EF sẽ không tin rằng nó không thể rỗng.
Archlight

2

Các câu trả lời trên là chính xác nếu bạn thực sự không có PK.

Nhưng nếu có một nhưng nó chỉ không được chỉ định với một chỉ mục trong DB và bạn không thể thay đổi DB (vâng, tôi làm việc trong thế giới của Dilbert), bạn có thể ánh xạ thủ công các trường thành khóa.


2

Đây chỉ là một bổ sung cho câu trả lời của @Erick T. Nếu không có cột nào có giá trị duy nhất, cách giải quyết là sử dụng khóa tổng hợp, như sau:

[Key]
[Column("LAST_NAME", Order = 1)]
public string LastName { get; set; }

[Key]
[Column("FIRST_NAME", Order = 2)]
public string FirstName { get; set; }

Một lần nữa, đây chỉ là một cách giải quyết. Giải pháp thực sự là sửa chữa mô hình dữ liệu.


2

Điều này có thể đến trễ để trả lời ... tuy nhiên ...

Nếu một bảng không có khóa chính thì sẽ có một vài kịch bản cần được phân tích để làm cho EF hoạt động chính xác. Quy tắc là: EF sẽ làm việc với các bảng / lớp với khóa chính. Đó là cách nó theo dõi ...

Nói, bảng của bạn 1. Bản ghi là duy nhất: tính duy nhất được tạo bởi một cột khóa ngoại duy nhất: 2. Bản ghi là duy nhất: tính duy nhất được tạo bởi sự kết hợp của nhiều cột. 3. Bản ghi không phải là duy nhất (đối với hầu hết các phần *).

Đối với các kịch bản # 1 và # 2, bạn có thể thêm dòng sau vào mô-đun DbContext Phương thức OnModelCreating: modelBuilder.Entity (). HasKey (x => new {x.column_a, x.column_b}); // càng nhiều cột càng cần thiết để tạo bản ghi duy nhất.

Đối với kịch bản # 3, bạn vẫn có thể sử dụng giải pháp trên (# 1 + # 2) sau khi bạn nghiên cứu bảng (* điều gì làm cho tất cả các bản ghi là duy nhất). Nếu bạn phải bao gồm TẤT CẢ các cột để làm cho tất cả các bản ghi là duy nhất thì bạn có thể muốn thêm một cột khóa chính vào bảng của mình. Nếu bảng này là của nhà cung cấp bên thứ 3 hơn là sao chép bảng này vào cơ sở dữ liệu cục bộ của bạn (qua đêm hoặc bao nhiêu lần bạn cần) với cột khóa chính được thêm tùy ý thông qua tập lệnh sao chép của bạn.


1
  1. Thay đổi cấu trúc Bảng và thêm Cột Chính. Cập nhật mô hình
  2. Sửa đổi tệp .EDMX trong Trình soạn thảo XML và thử thêm Cột mới dưới thẻ cho bảng cụ thể này (S NOT KHÔNG LÀM VIỆC)
  3. Thay vì tạo một Cột chính mới để thoát khỏi bảng, tôi sẽ tạo một khóa tổng hợp bằng cách liên quan đến tất cả các cột hiện có ( LÀM VIỆC )

Khung thực thể: Thêm DataTable không có Khóa chính cho Mô hình thực thể.


Tôi đã thử cách tiếp cận khóa tổng hợp với EF 4.0 và nó không hoạt động.
Ralph Willgoss

Cách tiếp cận này hoạt động hoàn hảo đối với tôi, có thể là một nỗi đau khi làm việc với các hệ thống cũ "đôi khi" ...
pinmonkeyiii

1

Cập nhật câu trả lời của @CodeNotFound .

Trong EF Core 3.0 DbQuery<T>đã không được chấp nhận, thay vào đó, bạn nên sử dụng các loại thực thể Keyless được cho là làm điều tương tự. Chúng được cấu hình với HasNoKey()phương thức ModelBuilder . Trong lớp DbContext của bạn, hãy làm điều này

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<YourEntityType>(eb =>
        {
            eb.HasNoKey();
        });

}

Có những hạn chế, đáng chú ý là:

  • Không bao giờ được theo dõi các thay đổi trong DbContext và do đó không bao giờ được chèn, cập nhật hoặc xóa trên cơ sở dữ liệu.
  • Chỉ hỗ trợ một tập hợp con các khả năng ánh xạ điều hướng, cụ thể:
    • Họ có thể không bao giờ đóng vai trò là người kết thúc chính của một mối quan hệ.
    • Họ có thể không có sự điều hướng đến các thực thể thuộc sở hữu
    • Chúng chỉ có thể chứa các thuộc tính điều hướng tham chiếu trỏ đến các thực thể thông thường.
    • Các thực thể không thể chứa các thuộc tính điều hướng đến các loại thực thể không cần khóa.

Điều này có nghĩa là cho câu hỏi về

Nếu tôi muốn sử dụng chúng và sửa đổi dữ liệu, tôi nhất thiết phải thêm PK vào các bảng đó hay có cách giải quyết nào để tôi không phải làm vậy không?

Bạn không thể sửa đổi dữ liệu theo cách này - tuy nhiên bạn có thể đọc. Người ta có thể hình dung bằng cách khác (ví dụ: ADO.NET, Dapper) để sửa đổi dữ liệu - đây có thể là một giải pháp trong trường hợp bạn hiếm khi cần thực hiện các thao tác không đọc và vẫn muốn gắn bó với EF Core cho các trường hợp đa số của bạn.

Ngoài ra, nếu bạn thực sự cần / muốn làm việc với các bảng heap (không cần khóa) - hãy xem xét việc bỏ qua EF và sử dụng một cách khác để nói chuyện với cơ sở dữ liệu của bạn.


0

Từ quan điểm thực tế, mỗi bảng - ngay cả một bảng không chuẩn hóa như bảng kho - nên có một khóa chính. Hoặc, không thành công, ít nhất nó phải có một chỉ mục duy nhất, không thể rỗng.

Nếu không có một số loại khóa duy nhất, các bản ghi trùng lặp có thể (và sẽ) xuất hiện trong bảng, điều này rất có vấn đề đối với cả các lớp ORM và cả về sự hiểu biết cơ bản của dữ liệu. Một bảng có các bản ghi trùng lặp có lẽ là một triệu chứng của thiết kế xấu.

Ít nhất, bảng ít nhất phải có một cột định danh. Việc thêm cột ID tự động tạo mất khoảng 2 phút trong SQL Server và 5 phút trong Oracle. Đối với một chút nỗ lực, nhiều, nhiều vấn đề sẽ được tránh.


Ứng dụng của tôi nằm trong cài đặt kho dữ liệu (với Oracle) và bạn đã thuyết phục tôi thực hiện trong 5 phút để thêm chỉ mục. Nó thực sự chỉ mất 5 phút (hoặc hơn một chút nếu bạn cần tra cứu hoặc sửa đổi ETL).
Trent

0

Chúng tôi cũng gặp phải vấn đề này và trong khi chúng tôi có một cột có null, điều quan trọng là chúng tôi có một cột phụ thuộc không có null và sự kết hợp của hai cột này là duy nhất.

Vì vậy, để trích dẫn câu trả lời được đưa ra bởi Pratap Reddy, nó hoạt động tốt với chúng tôi.


0

Tôi rất vui vì vấn đề của tôi đã được giải quyết.

Không thể cập nhật Entityset - vì nó có DefiningQuery và không tồn tại phần tử <UpdateFunction>

và làm điều này: Nhìn bên dưới dòng đó và tìm thẻ. Nó sẽ có một tuyên bố chọn ol 'lớn trong đó. Xóa thẻ và nội dung của nó ..

bây giờ mà không thay đổi DB TI có thể chèn vào bảng không có PK

cảm ơn tất cả và cảm ơn Pharylon



-8

Bảng chỉ cần có một cột không cho phép null

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.