2009-04-03 29 views
12

Tôi có một phương pháp làm thay đổi một "Tài khoản" đối tượng dựa trên các đại biểu hành động thông qua vào nó:Có cách nào dễ dàng để phân tích một chuỗi (biểu thức lambda) thành một đại biểu Action?

public static void AlterAccount(string AccountID, Action<Account> AccountAction) { 
    Account someAccount = accountRepository.GetAccount(AccountID); 
    AccountAction.Invoke(someAccount); 
    someAccount.Save(); 
} 

này hoạt động như dự định ...

AlterAccount("Account1234", a => a.Enabled = false); 

... nhưng bây giờ những gì tôi 'muốn cố gắng và làm là có một phương pháp như thế này:

public static void AlterAccount(string AccountID, string AccountActionText) { 
    Account someAccount = accountRepository.GetAccount(AccountID); 
    Action<Account> AccountAction = MagicLibrary.ConvertMagically<Action<Account>>(AccountActionText); 
    AccountAction.Invoke(someAccount); 
    someAccount.Save(); 
} 

sau đó nó có thể được sử dụng như:

AlterAccount("Account1234", "a => a.Enabled = false"); 

để vô hiệu hóa tài khoản "Account1234".

Tôi đã xem linq dynamic query library, dường như làm nhiều hơn hoặc ít hơn những gì tôi muốn nhưng đối với đại biểu loại Func, và kiến ​​thức về cây biểu hiện của tôi vv không đủ tốt để tìm hiểu cách đạt được Tôi muốn.

Có cách nào dễ dàng để làm những gì tôi muốn hay tôi cần học biểu thức đúng cách và viết mã tải không?

(Lý do tôi muốn làm điều này là để cho phép một cách dễ dàng của các đối tượng tài khoản hàng loạt cập nhật từ một kịch bản PowerShell nơi người dùng có thể chỉ định một biểu thức lambda để thực hiện các thay đổi.)

+0

cách cứng sẽ được sử dụng System.Reflection.Emit.DynamicMethod và tạo ra mã IL. tuy nhiên, trong trường hợp này, có thể dễ dàng chấp nhận cú pháp chỉ định một tên thuộc tính và một giá trị, và có một bộ thuộc tính cơ sở phản chiếu. – eulerfx

+0

Vâng, tôi đã nghĩ về điều đó, nhưng tôi nghĩ rằng sẽ loại trừ một số thứ có khả năng mạnh mẽ, như có thể gọi AlterAccount ("Account1234", "a => a.Enabled =! A.Enabled"); (không chắc chắn lý do tại sao bạn muốn làm điều đó đặc biệt, nhưng có những ví dụ hợp lý hơn) – Whisk

+2

Tôi đã có một tiếng cười tốt đọc này: 'MagicLibrary.ConvertMagically' –

Trả lời

1

Không có cách tổng quát để phân tích một chuỗi thành một biểu thức lambda mà không có một trình biên dịch đầy đủ, bởi vì các biểu thức lambda có thể tham chiếu đến những thứ được định nghĩa bên ngoài biểu thức lambda. Tôi biết không có thư viện nào xử lý trường hợp cụ thể mà bạn muốn. Có một cuộc thảo luận dài về điều này trên thread trên một nhóm thảo luận C#.

Cách dễ nhất để có được những gì bạn muốn là biên dịch một phương thức khi chạy. Bạn có thể viết một hàm có trong chuỗi "a.Enabled = true; return a;" và dính vào giữa một hàm lấy Tài khoản làm tham số. Tôi sẽ sử dụng library làm điểm xuất phát, nhưng bạn cũng có thể sử dụng chức năng được đề cập trên another thread.

+0

Cảm ơn - Từ một thử nhanh mà dường như không hoạt động khi đánh giá một chuỗi như "a => a.Enabled = true" cho một đại biểu Action - tôi có đang làm gì sai hay nó sẽ không hoạt động vì đó không phải là một câu lệnh C# hoàn chỉnh? Tôi đã cập nhật câu hỏi để làm rõ. – Whisk

+0

rossfabricant; thư viện LINQ động mà ông đề cập thực hiện chính xác điều đó. Tất nhiên, bạn phải cung cấp thông tin đánh máy khi bạn đi để biên dịch, nhưng nó biên dịch tốt. – MichaelGG

+0

Nó có thể xây dựng các truy vấn nhưng tôi không thấy rằng nó có thể xây dựng bất cứ điều gì mà sửa đổi nhà nước. – RossFabricant

3

Thư viện LINQ động là một lựa chọn tốt, vì nó sẽ tạo ra các biểu thức mà bạn có thể biên dịch thành mã theo kiểu gọn nhẹ.

Ví dụ bạn đã cung cấp thực sự tạo ra boolean - vì vậy bạn có thể yêu cầu Func và có thể sắp xếp nó.

Chỉnh sửa: Tất nhiên điều này là sai, vì Biểu thức không có bài tập gì cả.

Vì vậy, một cách tiềm năng khác là lấy hai lambdas. Một để tìm các thuộc tính bạn muốn một để cung cấp một giá trị:

(a => a.AccountId), (a => true)

Sau đó dùng phản ánh để thiết lập thuộc tính tham chiếu trong lambda đầu tiên với kết quả của câu thứ hai. Hackish, nhưng nó vẫn có thể nhẹ so với việc gọi trình biên dịch C#.

Bằng cách này bạn không phải tự mình làm nhiều việc - các biểu thức bạn nhận được sẽ chứa hầu hết mọi thứ bạn cần.

+0

Tôi nghĩ ... Nếu tôi đang làm "a => a.Enabled == false" thì nó sẽ hoạt động, nhưng tôi đang làm "a => a.Enabled = false" - Tôi muốn thực hiện tác vụ đó trên Tài khoản, vì vậy tôi không nghĩ rằng nó sẽ làm việc mà không có một số sửa đổi. – Whisk

+0

Bah bạn nói đúng. Nó hoạt động cho funcs: Func exp =() => test = false; nhưng không phải cho Biểu thức, vì họ không có cách nào đại diện cho bài tập. – MichaelGG

+0

Đúng - Tôi nghĩ rằng tôi có thể đi xuống con đường cố gắng sửa đổi các công cụ Dynamic LINQ để làm việc với Action cũng như Func ... – Whisk

0

Đó là dễ dàng:

  • Sử dụng CodeDom để tạo ra các mô-đun có chứa các "lớp xung quanh" bạn sẽ sử dụng để xây dựng các biểu thức; lớp này phải triển khai giao diện được biết đến với ứng dụng của bạn
  • Sử dụng CodeSnippedExpression để chèn biểu thức vào thành viên của nó.
  • Sử dụng Activator nhập để tạo phiên bản của lớp này trong thời gian chạy.

Về cơ bản, bạn cần xây dựng các lớp sau đây với CodeDOM:

using System; 
using MyNamespace1; 
using ... 
using MyNamespace[N]; 

namespace MyNamespace.GeneratedTypes 
{ 
    public class ExpressionContainer[M] : IHasAccountAction 
    { 
    public Action<Account> AccountAction { 
     get { 
     return [CodeSnippedExpression must be used here]; 
     } 
    } 
    } 
} 

Giả sử rằng IHasAccountAction là:

public IHasAccountAction { 
    public Action<Account> AccountAction { get; } 
} 

Nếu đây được thực hiện, bạn có thể nhận sự biểu hiện tổng hợp từ chuỗi một cách dễ dàng. Nếu bạn cần biểu diễn cây biểu thức của nó, hãy sử dụng Expression<Action<Account>> thay vì Action<Account> trong loại được tạo.

+0

(trả lời bình luận của bạn về các nhà khai thác chung, btw) –

3

Bạn có thể thử này: Dynamic Lambda Expressions Using An Isolated AppDomain

Nó biên dịch một biểu thức lambda sử dụng trình biên dịch CodeDOM. Để vứt bỏ lắp ráp trong bộ nhớ được tạo ra, trình biên dịch chạy trên AppDomain bị cô lập. Để chuyển biểu thức qua ranh giới miền, nó phải được tuần tự hóa. Than ôi, Expression<> không phải là Serializable. Vì vậy, một thủ thuật phải được sử dụng. Tất cả các chi tiết được giải thích trong bài viết.

Tôi là tác giả của thành phần đó, nhân tiện. Tôi rất muốn nghe phản hồi của bạn từ nó.

1

Bạn có thể sử dụng Spring.NET của lambda expression evaluation engine

Biểu thức có thể là một chút khác nhau, nhưng bạn vẫn sẽ sử dụng một String cho biểu.

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