2014-06-11 23 views
11

Tôi đang sử dụng EF6 với dự án cơ sở dữ liệu đầu tiên. Chúng tôi có một yêu cầu để sử dụng chuỗi là một tính năng được giới thiệu trong máy chủ SQL 2012 (tôi tin).Entity Framework 6 và SQL Server Sequences

Trên bàn cột sắc có một giá trị mặc định sử dụng:

(NEXT VALUE FOR [ExhibitIdentity]) 

này được sử dụng như chúng ta có hai bảng chứa các thông tin triển lãm cho các phòng ban riêng biệt nhưng chúng ta cần phải nhận dạng phải là duy nhất trên cả hai các bảng như sau đó được sử dụng làm tham chiếu trong nhiều bảng chung được chia sẻ khác.

Sự cố của tôi đang sử dụng tính năng này trong Khuôn khổ thực thể, tôi đã googled nhưng không thể tìm thấy nhiều thông tin liên quan đến việc liệu EF6 có hỗ trợ chúng hay không. Tôi đã thử đặt StoreGeneratedPatttern trong EFdesigner thành Identity nhưng khi lưu điều này sẽ không có hàng bị ảnh hưởng vì đang sử dụng scope_identity để xem chèn có thành công hay không.

Đặt thành được tính ném lỗi nói rằng tôi nên đặt nó thành nhận dạng và đặt nó thành không có nguyên nhân khiến nó chèn 0 làm giá trị id và không thành công.

Tôi có cần gọi hàm/thủ tục để nhận chuỗi tiếp theo và sau đó gán nó cho giá trị id trước khi lưu bản ghi?

Bất kỳ trợ giúp nào được đánh giá cao.

Trả lời

16

Rõ ràng là bạn không thể thoát khỏi trò chơi bắt số 22 này bằng cách chơi với DatabaseGeneratedOption s.

Tùy chọn tốt nhất, như bạn đã đề xuất, là đặt DatabaseGeneratedOption.None và nhận giá trị tiếp theo từ chuỗi (ví dụ: trong this question) ngay trước khi bạn lưu bản ghi mới. Sau đó gán nó cho giá trị Id và lưu lại. Điều này là đồng thời an toàn, bởi vì bạn sẽ là người duy nhất vẽ giá trị cụ thể đó từ trình tự (giả sử không có ai đặt lại chuỗi).

Tuy nhiên, có một có thể hack ...

Một một xấu, và tôi nên dừng lại ở đây ...

EF 6 giới thiệu lệnh đánh chặn API. Nó cho phép bạn thao tác các lệnh SQL của EF và các kết quả của chúng trước và sau khi các lệnh được thực hiện. Tất nhiên chúng ta không nên giả mạo các lệnh này, phải không?

Vâng ... nếu chúng ta nhìn vào một lệnh chèn được thực hiện khi DatabaseGeneratedOption.Identity được thiết lập, chúng ta thấy một cái gì đó như thế này:

INSERT [dbo].[Person]([Name]) VALUES (@0) 
SELECT [Id] 
FROM [dbo].[Person] 
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity() 

Lệnh SELECT được sử dụng để lấy ra giá trị khóa chính từ cơ sở dữ liệu và đặt thuộc tính nhận dạng của đối tượng mới thành giá trị này. Điều này cho phép EF sử dụng giá trị này trong các câu lệnh chèn tiếp theo đề cập đến đối tượng mới này bằng khóa ngoại trong cùng một giao dịch.

Khi khóa chính được tạo theo mặc định lấy giá trị của nó từ một chuỗi (như bạn làm), điều hiển nhiên là không có scope_identity().tuy nhiên có một giá trị hiện tại của chuỗi, có thể được tìm thấy bởi một lệnh như

SELECT current_value FROM sys.sequences WHERE name = 'PersonSequence' 

Nếu duy nhất chúng ta có thể làm cho EF thực hiện lệnh này sau khi chèn thay vì scope_identity()!

Vâng, chúng tôi có thể.

Đầu tiên, chúng ta phải tạo ra một lớp mà thực hiện IDbCommandInterceptor, hoặc được thừa hưởng từ việc thực hiện mặc định DbCommandInterceptor:

using System.Data.Entity.Infrastructure.Interception; 

class SequenceReadCommandInterceptor : DbCommandInterceptor 
{ 
    public override void ReaderExecuting(DbCommand command 
      , DbCommandInterceptionContext<DbDataReader> interceptionContext) 
    { 
    } 
} 

Chúng tôi thêm lớp này với bối cảnh chặn bởi các lệnh

DbInterception.Add(new SequenceReadCommandInterceptor()); 

Lệnh ReaderExecuting chạy ngay trước khi command được thực thi. Nếu đây là lệnh INSERT có cột nhận dạng, văn bản của nó trông giống như lệnh trên. Bây giờ chúng ta thể thay thế các phần scope_identity() bởi truy vấn nhận được giá trị chuỗi hiện tại:

command.CommandText = command.CommandText 
          .Replace("scope_identity()", 
          "(SELECT current_value FROM sys.sequences 
           WHERE name = 'PersonSequence')"); 

Bây giờ lệnh sẽ trông giống như

INSERT [dbo].[Person]([Name]) VALUES (@0) 
SELECT [Id] 
FROM [dbo].[Person] 
WHERE @@ROWCOUNT > 0 AND [Id] = 
    (SELECT current_value FROM sys.sequences 
    WHERE name = 'PersonSequence') 

Và nếu chúng ta chạy này, điều buồn cười là : nó hoạt động. Ngay sau lệnh SaveChanges đối tượng mới đã nhận được giá trị Id liên tục của nó.

Tôi thực sự không nghĩ đây là sản phẩm sẵn sàng. Bạn sẽ phải sửa đổi lệnh khi đó là lệnh chèn, chọn trình tự đúng dựa trên thực thể được chèn, tất cả bằng thao tác chuỗi bẩn ở một nơi khá mơ hồ. Tôi không biết nếu có đồng thời nặng, bạn sẽ luôn nhận được giá trị chuỗi đúng. Nhưng ai biết được, có thể một phiên bản tiếp theo của EF sẽ hỗ trợ việc này.

+2

Có thể trích xuất toàn bộ 'select' và đặt mệnh đề' output inserted.Id' ngay trước 'values'could the trick thay vì, theo một cách mạnh mẽ hơn. –

+2

@Frederic Thú vị, nhưng câu lệnh chọn cũng được sử dụng để lấy giá trị của các cột được tính toán (nếu có). Và mệnh đề 'output' phải có trong biến' into' nếu bảng có các trigger, làm cho nó phức tạp hơn nhiều (nếu có thể) để lấy giá trị id được chèn vào. –

+2

'output' tuyên bố có thể trả về cột tính toán quá. Điểm tốt để hỗ trợ kích hoạt, chúng ta phải sử dụng 'vào'. Vì vậy, hack sẽ thêm dòng đầu tiên là 'declare @InsId table (Id int)', chèn 'output inserted.Id vào @ InsId', giữ' select' nhưng được tăng cường với 'inner join @InsId ii trên [Person] . [Id] = ii.Id' và cuối cùng loại bỏ điều kiện 'và [Id] = ...'. Bắt đầu hơi nặng nề cho một bản hack xấu. –

Các vấn đề liên quan