I do not see a solution that solves all your limitations. This is one of the caveats using ORM-mapper, you do not control the generated SQL.
In this case, if it is completely unacceptable for you to send several queries to the database, the harsh truth is that you have to write the query yourself.
Update
I became curious and created an extension method that can do this! Of course, he creates his own SQL command, and it just works for Linq2SQL. Also a massive disclaimer: this is pretty dirty code, if I have time, I will fix it on 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()
source share