2013-06-26 9 views
6

Mi chiedo se JoinSqlBuilder di ServiceStack.OrmLite permettono di costruire la seguente query semplice:fa ServiceStack.OrmLite.JoinSqlBuilder permettono di costruire una semplice query

SELECT * FROM Table1 a 
    INNER JOIN Table2 b ON ... 
    WHERE a.Column1 = 1 AND (a.Column2 = 2 OR b.Column3 = 3); 

Il problema è quello di costruire (a.Column2 = 2 OR b.Column3 = 3) parte. JoinSqlBuilder ha un elenco di metodi come Where<T>, And<T>, Or<T> che consentono di aggiungere condizioni per una query.

Per esempio, se faccio:

builder 
    .Join(...) 
    .Where<Table1Poco>(a => a.Column1 == 1) 
    .And<Table1Poco>(a => a.Column2 == 2) 
    .Or<Table2Poco>(a => a.Column3 == 3) 
    ...; 

mi metterò:

... WHERE a.Column1 = 1 AND a.Column2 = 2 OR b.Column3 = 3; 

Esiste un modo per costruire a.Column1 = 1 AND (a.Column2 = 2 OR b.Column3 = 3) con ServiceStack.OrmLite?

So che posso farlo con raw sql ma non è un'opzione in quanto non voglio perdere la sicurezza del tipo e l'indipendenza del dialetto.

+1

Per quanto ne so non è lì. E Micro-orm è buono per questo tipo o cose. Puoi ricorrere alla semplice vecchia query per scenari complicati. Spero che tu sappia la funzione Query () che puoi usare e passare un parametro se lo desideri. – kunjee

risposta

4

Sono d'accordo con kunjee sul fatto che questo non è davvero qualcosa a cui un Micro-orm è adatto. Detto questo, posso pensare a 2 potenziali opzioni ... nessuna delle quali è davvero qualcosa che consiglierei su un ORM completo (EF o NHibernate) come soluzione. Ma forse questo aiuterà a sollecitare opzioni migliori.

Opzione 1 - Creare una stringa di clausola Where utilizzando la riflessione per mantenere un po 'di sicurezza di tipo. Avrai comunque bisogno di scrivere un po 'di SQL.

Esempio

var jn = new JoinSqlBuilder<Table1, Table2>(); 
jn = jn.Join<Table1, Table2>(s => s.Column1, d => d.Field1); 

//using ExpressionVisitor because I didn't see a way to allow a Where clause string parameter to be used 
//on a JoinSqlBuilder method 
var ev = OrmLiteConfig.DialectProvider.ExpressionVisitor<Table1>(); 
ev.Where(
    SqlHelper.ToSqlField<Table1>(x => x.Column1) + "={0} AND (" + 
    SqlHelper.ToSqlField<Table1>(x => x.Column2) + "={1} OR " + SqlHelper.ToSqlField<Table2>(x => x.Column3) + 
     "={2})", "1", "2", "3"); 

var sql = jn.ToSql() + ev.WhereExpression; 

classe helper

public static class SqlHelper 
{ 
    public static string ToSqlField<T>(Expression<Func<T, object>> expression) 
    { 
     //This should return something like 'Table1.Column1' 
     return typeof(T).Name + "." + GetMemberInfo(expression).Name; 
    } 

    // Stolen from FluentNHibernate.ReflectionUtility 
    public static MemberInfo GetMemberInfo<TEntity>(Expression<Func<TEntity, object>> expression) 
    { 
     MemberInfo memberInfo = null; 

     switch (expression.Body.NodeType) 
     { 
      case ExpressionType.Convert: 
       { 
        var body = (UnaryExpression)expression.Body; 
        if (body.Operand is MethodCallExpression) 
        { 
         memberInfo = ((MethodCallExpression)body.Operand).Method; 
        } 
        else if (body.Operand is MemberExpression) 
        { 
         memberInfo = ((MemberExpression)body.Operand).Member; 
        } 
       } 
       break; 
      case ExpressionType.MemberAccess: 
       memberInfo = ((MemberExpression)expression.Body).Member; 
       break; 
      default: 
       throw new ArgumentException("Unsupported ExpressionType", "expression"); 
     } 

     if (memberInfo == null) { throw new ArgumentException("Could not locate MemberInfo.", "expression"); } 

     return memberInfo; 
    } 
} 

Opzione 2 - Mess/inquinare le vostre classi e spegnere prefissi tavolo in un ExpressionVisitor per consentire al SQL corretta per essere generato. Questo esploderà completamente se 2 classi hanno la stessa proprietà e sono usate in una clausola Where.

//Modify Table1 to include a reference to Table2 
public class Table1 
{ 
    public string Column1 { get; set; } 
    public string Column2 { get; set; } 

    [ServiceStack.DataAnnotations.Ignore] 
    public Table2 Table2 { get; set; } 
} 

var ev = OrmLiteConfig.DialectProvider.ExpressionVisitor<Table1>(); 
ev.PrefixFieldWithTableName = false; 

var jn = new JoinSqlBuilder<Table1, Table2>(); 
jn = jn.Join<Table1, Table2>(s => s.Column1, d => d.Field1); 
ev.Where(x => x.Column1 == "1"); 
ev.Where(x => x.Column2 == "2" || ((Table2)x.Table2).Column3 == "3"); //do cast to avoid InvalidOperationException 

var sql = jn.ToSql() + ev.WhereExpression; 
+0

Grazie per la risposta completa. Sono arrivato a smth simile all'opzione 1 - per scrivere il mio generatore di query su ExpressionVisitor di OrmLite. Non sono d'accordo sul fatto che i join non debbano essere un compito per micro orm perché i join sono in realtà il nucleo dei database relazionali stessi. Non ho un join molto complesso qui. La prima cosa che voglio da orm è la completa indipendenza del dialetto, in modo che raw sql non sia un'opzione. Il secondo è possibile (ma non richiesto) typesafety. Il secondo punto riguarda solo la gestione degli DTO. È solo un'opposizione. Grazie ancora per una risposta. – ILya