简体   繁体   中英

Independent subqueries in SQL to Linq statement (hit DB only one time)

Having something similar to:

 SELECT (SELECT COUNT(*) from Table1),(SELECT COUNT(*) from Table2 )

How do I write it in linq? Or is it simple not possible?

Limitations :

  • Can only hit the database one time:

      var result = new { Sum1 = db.Table1.Count(), Sum2 = db.Table2.Count() }); // is not valid..... 
  • I do not want to use something similar to (using a "helping" table):

     var result = (from t3 in db.Table3 select new { Sum1 = db.Table1.Count(), Sum2 = db.Table2.Count() }).firstOrDefault(); //In order to get only the first row //but it will not return nothing if the table 3 has no entries...... 
  • Not using db.Database.ExecuteSqlCommand

I cannot see a solution which solves all your limitations. This is one of the caveats with using an ORM-mapper, you are not in control of the generated SQL.

In this case, if it is utterly unacceptable for you to send more than one query to the database, the harsh truth is that you will have to write the query yourself.

Update

I got curious and created an extension method that can do this! Of course it constructs its own SQL command, and it just works for Linq2SQL. Also massive disclaimer: It's fairly dirty code, if I have some time I'll fix it up in the weekend :)

public static TOut CountMany<TContext, TOut>(this TContext db, Expression<Func<TContext, TOut>> tableSelector)
        where TContext: DataContext
    {
        var newExpression = (NewExpression) tableSelector.Body;
        var tables =
            newExpression.Arguments.OfType<MethodCallExpression>()
                         .SelectMany(mce => mce.Arguments.OfType<MemberExpression>())
                         .ToList();
        var command = new string[tables.Count];
        for(var i = 0; i < tables.Count; i++)
        {
            var table = tables[i];
            var tableType = ((PropertyInfo) table.Member).PropertyType.GetGenericArguments()[0];
            var tableName = tableType.GetCustomAttribute<TableAttribute>().Name;
            command[i] = string.Format("(SELECT COUNT(*) FROM {0}) AS T{1}", tableName, i);
        }

        var dbCommand = db.Connection.CreateCommand();
        dbCommand.CommandText = string.Format("SELECT {0}", String.Join(",", command));
        db.Connection.Open();
        IDataRecord result;
        try
        {
            result = dbCommand.ExecuteReader().OfType<IDataRecord>().First();
        }
        finally
        {
            db.Connection.Close();
        }

        var results = new object[tables.Count];
        for (var i = 0; i < tables.Count; i++)
            results[i] = result.GetInt32(i);
        var ctor = typeof(TOut).GetConstructor(Enumerable.Repeat(typeof(int), tables.Count).ToArray());
        return (TOut) ctor.Invoke(results);
    }

the code is called like this:

var counts = dbContext.CountMany(db => new 
                                       {
                                           table1Count = db.Table1.Count(),
                                           table2Count = db.Table2.Count()
                                           //etc.
                                       });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM