2011-12-13 16 views
15

Biorąc pod uwagę tablicę wartości, chciałbym utworzyć anonimowy obiekt z właściwościami opartymi na tych wartościach. Nazwy właściwości będą po prostu "pN", gdzie N jest indeksem wartości w tablicy.Jak utworzyć anonimowy obiekt z nazwami właściwości określonymi dynamicznie?

Na przykład, biorąc pod uwagę

object[] values = { 123, "foo" };

Chciałbym utworzyć obiekt anonimowy

new { p0 = 123, p1 = "foo" };

Jedynym sposobem mogę myśleć to zrobić byłoby używanie switch lub if łańcuch do rozsądnej liczby parametrów do obsługi, ale zastanawiałem się, czy był bardziej elegancki sposób to zrobić:

object[] parameterValues = new object[] { 123, "foo" }; 
dynamic values = null; 

switch (parameterValues.Length) 
{ 
    case 1: 
     values = new { p0 = parameterValues[0] }; 
     break; 
    case 2: 
     values = new { p0 = parameterValues[0], p1 = parameterValues[1] };  
     break; 
    // etc. up to a reasonable # of parameters 
} 

Tło

Mam istniejący zestaw metod, które wykonują instrukcje SQL przeciwko bazy danych. Metody zwykle pobierają string dla instrukcji sql i params object[] dla parametrów, jeśli takie istnieją. Rozumie się, że jeśli zapytanie używa parametrów, będą one nazywane @p0, @p1, @p2, etc..

Przykład:

public int ExecuteNonQuery(string commandText, CommandType commandType, params object[] parameterValues) { .... } 

które można nazwać tak:

db.ExecuteNonQuery("insert into MyTable(Col1, Col2) values (@p0, @p1)", CommandType.Text, 123, "foo"); 

Teraz chciałbym użyć Dapper w tej klasie do zawijania i narazić Query<T> metodę Wytworny, a zrobić to w sposób sposób zgodny z istniejącymi metodami, np coś takiego:

public IEnumerable<T> ExecuteQuery<T>(string commandText, CommandType commandType, params object[] parameterValues) { .... } 

ale Query<T> metoda Dapper przybiera wartości parametrów w anonimowej obiektu:

var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid }); 

prowadzące do mojego pytania na temat tworzenia anonimowego obiekt przekazać parametry do Dapper.


Dodawanie kodu przy użyciu klasy DynamicParameter wymagane przez @Paolo Tedesco.

string sql = "select * from Account where Id = @p0 and username = @p1"; 
dynamic values = new DynamicParameter(123, "test"); 
var accounts = SqlMapper.Query<Account>(connection, sql, values); 

zgłasza wyjątek w linii 581 pliku SqlMapper.cs Dapper za:

using (var reader = cmd.ExecuteReader()) 

i wyjątek jest SqlException:

Musi zadeklarować zmienną skalarne "@ p0".

i sprawdzanie właściwości cmd.Parameters nie wykazują parametrów skonfigurowanych dla polecenia.

+0

Tak, niewłaściwie używasz DynamicParameter, patrz wiersz 243: http://code.google.com/p/dapper-dot-net/source/browse/Tests/Tests.cs –

+0

również, 'IDynamicParamters' zapewnia absolutną elastyczność. Jeśli chodzi o korzyści dla dapperów, nigdy nie powinieneś potrzebować pieczenia typów anonowych, jako doświadczenia w nauce, które możesz, ale nie jest to warte wysiłku. W szczególności możesz wypalić silny typ z ICollection, jeśli naprawdę chcesz używać emisji. –

Odpowiedz

5

Niezupełnie anonimowy obiekt, ale co z implementacją DynamicObject, która zwraca wartości dla p1 ... pn na podstawie wartości w tablicy? Czy to działałoby z Dapper?

Przykład:

using System; 
using System.Dynamic; 
using System.Text.RegularExpressions; 

class DynamicParameter : DynamicObject { 

    object[] _p; 

    public DynamicParameter(params object[] p) { 
     _p = p; 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) { 
     Match m = Regex.Match(binder.Name, @"^p(\d+)$"); 
     if (m.Success) { 
      int index = int.Parse(m.Groups[1].Value); 
      if (index < _p.Length) { 
       result = _p[index]; 
       return true; 
      } 
     } 
     return base.TryGetMember(binder, out result); 
    } 

} 

class Program { 
    static void Main(string[] args) { 
     dynamic d1 = new DynamicParameter(123, "test"); 
     Console.WriteLine(d1.p0); 
     Console.WriteLine(d1.p1); 
    } 
} 
+0

+1, dziękuję, to jest świetne, ale niestety Dapper tego nie lubi :(kończy się próbą wykonania komendy db bez żadnych parametrów - nie wiem jeszcze dlaczego, ale myślę, że to stara się ocenić Instancja 'DynamicParameter' jako wartość parametru –

+0

@adrift: czy możesz umieścić mały przykład kodu, który pokazuje twój problem? –

+0

edytował pytanie, aby dołączyć kod pokazujący wyjątek wygenerowany podczas próby użycia' DynamicParameter'. –

1

Nie można dynamicznie tworzyć anonimowe obiekty. Ale Dapper powinien pracować z dynamicznym obiektem. Aby stworzyć dynamiczne obiekty w miły sposób, możesz użyć Clay. To pozwala na pisanie kodu jak

var person = New.Person(); 
person["FirstName"] = "Louis"; 
// person.FirstName now returns "Louis" 
12

Jesteś nadużywania Dapper, nigdy nie należy trzeba to zrobić, zamiast albo wdrożyć IDynamicParameters lub użyć konkretnego niezwykle elastyczne DynamicParameters klasę.

W szczególności:

string sql = "select * from Account where Id = @id and username = @name"; 
var values = new DynamicParameters(); 
values.Add("id", 1); 
values.Add("name", "bob"); 
var accounts = SqlMapper.Query<Account>(connection, sql, values); 

DynamicParameters może wziąć w anonimowej klasy w konstruktorze. Możesz dołączyć DynamicParameters używając metody AddDynamicParams.

Co więcej, nie ma ścisłej zależności od typów anonowych. Wytworny pozwoli na konkretnych typów jak params np

class Stuff 
{ 
    public int Thing { get; set; } 
} 

... 

cnn.Execute("select @Thing", new Stuff{Thing = 1}); 

Kevin miał podobny pytanie: Looking for a fast and easy way to coalesce all properties on a POCO - DynamicParameters doskonale współpracuje również tutaj bez żadnej potrzeby magicznym obręcz skoków.

Powiązane problemy