Đây là ví dụ triển khai câu trả lời được đề xuất bởi @TheCloudlessSky. Tôi nghĩ rằng nó sẽ giúp đỡ bất cứ ai đang tự hỏi làm thế nào để đi về việc thực hiện nó.
Tôi đã làm việc với một cơ sở dữ liệu hiện có, do đó, lớp mô hình cơ bản được tạo tự động cho tôi.
User.cs Auto tạo:
namespace MyApp.Model
{
public partial class User
{
public int UserId { get; set; }
public byte[] SSN { get; set; }
...
}
}
Tôi tạo ra User.cs. của riêng tôi (Lưu ý nó nằm trong cùng một không gian tên như User.cs được tạo tự động và không có lỗi trình biên dịch do User.cs được tạo tự động được khai báo là một phần lớp! Ngoài ra, User.cs của riêng tôi không thể nằm trong cùng thư mục với User.cs tự động tạo ra vì tên tập tin xung đột!)
namespace MyApp.Model
{
public partial class User
{
public string DecryptedSSN { get; set; }
...
}
}
Bây giờ bất cứ khi nào tôi được lấy tài từ DbContext của tôi, tôi sẽ thấy tất cả các thuộc tính được định nghĩa trong lớp tự động tạo ra cũng như những quy định tại lớp nâng cao của tôi.
Dưới đây là triển khai UserRepository của tôi.cs:
namespace MyApp.Model
{
public interface IUserRepository
{
User Get(int userId);
...
}
public class UserRepository : IUserRepository
{
public User GetById(int userId)
{
using (var dataContext = MyDbContext())
{
var user = dataContext.Users.Find(u => u.UserId == userId);
var decryptedSSNResult = dataContext.Decrypt(u.SSN);
user.DecryptedSSN = decryptedSSNResult.FirstOrDefault();
return user;
}
}
}
}
Bây giờ bạn có thể tự hỏi làm cách nào/ở đâu tôi nhận được MyDbContext.Decrypt() từ?
Điều này KHÔNG được tạo tự động cho bạn. Tuy nhiên, bạn có thể nhập thủ tục được lưu trữ này vào tệp Model.Context.cs được tạo tự động của bạn. (Quy trình này được viết rất rõ trong bài viết EntityFramework chính thức: Cách: Nhập một Quy trình được Lưu trữ (Công cụ Mô hình Dữ liệu Thực thể) tại http://msdn.microsoft.com/en-us/library/vstudio/bb896231(v=vs.100).aspx)
Chỉ trong trường hợp bạn không biết kết quả cuối cùng sẽ trông như thế nào, đây là những gì đã tự động tạo ra trong Model.Context.cs tôi:
namespace MyApp.Model
{
// using statements found here
public partial class MyDbContext : DbContext
{
public MyDbContext()
: base("name = MyDbContext")
{ }
public virtual ObjectResult<string> Decrypt(byte[] encryptedData)
{
var encryptedDataParameter = encryptedData != null ?
new ObjectParameter("encryptedData", encryptedData) :
new ObjectParameter("encryptedData", typeof(byte[]));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<string>("Decrypt", encryptedDataParameter);
}
// similar function for Encrypt
}
}
Đây là cách Decrypt tôi Stored Procedure trông giống như:
CREATE PROCEDURE decrypt
@encryptedData VARBINARY(8000)
AS
BEGIN
OPEN SYMMETRIC KEY xxx_Key DECRYPTION BY CERTIFICATE xxx_Cert;
SELECT CAST(DECRYPTIONBYKEY(@encryptedData) AS NVARCHAR(MAX)) AS data;
CLOSE ALL SYMMETRIC KEYS;
END;
GO
Cân nhắc hiệu suất
Bây giờ tôi đã cho bạn thấy một câu trả lời được đưa ra bởi @TheCloudlessSky, tôi sẽ nhanh chóng làm nổi bật một số điểm liên quan đến hiệu suất.
1) Mỗi khi bạn truy xuất đối tượng người dùng, có 2 chuyến đi được thực hiện cho cơ sở dữ liệu thay vì 1. Chuyến đi đầu tiên để truy xuất đối tượng; chuyến đi thứ hai để giải mã SSN. Điều này có thể gây ra vấn đề hiệu suất nếu bạn không cẩn thận.
Đề xuất: KHÔNG tự động giải mã các trường được mã hóa! Trong ví dụ của tôi được hiển thị ở trên, tôi đã giải mã SSN khi tôi đang truy xuất đối tượng người dùng. Tôi đã làm điều đó chỉ nhằm mục đích trình diễn! Hãy tự hỏi nếu bạn thực sự cần SSN mỗi lần người dùng được truy xuất. Nếu có thể, hãy chọn giải mã lười biếng qua giải mã háo hức!
2) Trong khi tôi chưa chứng minh điều này, mỗi lần bạn tạo/cập nhật đối tượng người dùng, cũng sẽ có 2 chuyến đi được thực hiện cho cơ sở dữ liệu. Chuyến đi đầu tiên để mã hóa SSN; chuyến đi thứ hai để chèn đối tượng. Một lần nữa điều này có thể gây ra vấn đề hiệu suất nếu bạn không cẩn thận.
Đề xuất: Hãy ý thức về hiệu suất này nhưng không ủy quyền mã hóa và lưu SSN làm phương pháp khác. Giữ tất cả trong một hoạt động nếu không bạn có thể quên lưu nó hoàn toàn. Vì vậy, các khuyến nghị cho việc tạo/cập nhật là đối diện của lấy: chọn mã hóa háo hức trên mã hóa lười biếng!
Có vẻ như một giải pháp tốt và tôi có thể sẽ làm với điều đó nếu tôi tạo cơ sở dữ liệu mới. Vấn đề là, cơ sở dữ liệu khá lớn và được sử dụng bởi rất nhiều dự án vì vậy nó sẽ là một công việc rất lớn để đi qua mã và thay đổi điều này. – Andreas
@Andreas - Xem chỉnh sửa của tôi ở trên. – TheCloudlessSky
Cảm ơn bạn đã đề xuất. Điều này trông giống như một ý tưởng thực sự tốt và tôi đã thử nó. thật không may nếu tôi muốn có thể thực hiện truy vấn LINQ trên tất cả dữ liệu được giải mã của tôi từ bảng, thủ tục được lưu trữ trước tiên phải thực thi. Điều này mất mãi mãi vì nó là 250 000+ hàng với 5 cột mỗi được giải mã. Vì vậy, làm điều này: context.AllMembers(). Trường hợp (x => x.MemberId == 1) sẽ mất quá nhiều thời gian. Chắc chắn tôi có thể làm một SP mà có một đối số của memberid, nhưng nếu tôi muốn tìm kiếm trên ví dụ tên đầu tiên với LINQ? Có lẽ tôi đang thiếu một số thứ quan trọng ở đây ... – Andreas