I'm trying to implement paging within my project.
When using NHibernate's IQueryOver syntax as shown below things are working as expected.
public PagedResult<T> ExecutePagedQuery(IQueryOver<T,T> query)
{
SetPaging(query);
var results = query.Future<T>();
var count = query.ToRowCountQuery().FutureValue<int>();
return new PagedResult<T>()
{
TotalItemCount = count.Value,
PageOfResults = results.ToList()
};
}
protected virtual void SetPaging(IQueryOver<T, T> query)
{
var maxResults = PageSize;
var numberToSkip = (PageNumber - 1) * PageSize;
query.Skip(numberToSkip).Take(maxResults);
}
For some queries we are using HQL instead of the IQueryOver syntax however.
I'm wondering if there is an equivalent to "query.ToRowCountQuery().FutureValue< int >()" that can be used when querying when passing an IQuery insead of an IQueryOver to provide the row count.
public PagedResult<T> ExecutePagedQuery(IQuery query)
{
SetPaging(query);
var results = query.Future<T>();
var count = // ToRowCountQueryEquivalent?
return new PagedResult<T>()
{
TotalItemCount = count,
PageOfResults = results.ToList()
};
}
protected virtual void SetPaging(IQuery query)
{
var maxResults = PageSize;
var numberToSkip = (PageNumber - 1) * PageSize;
query.SetFirstResult(numberToSkip);
query.SetMaxResults(maxResults);
}
some crazy idea
string countQuery;
if (query.QueryString.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
countQuery = Regex.Replace(query.QueryString, "SELECT (.*) FROM", #"SELECT Count($1) FROM", RegexOptions.IgnoreCase);
else
countQuery = "SELECT COUNT(*) " + query.QueryString;
We've since converted our HQL queries to IQueryOver allowing us to take advantage of the ToRowCountQuery() helper.
Related
How do I run a raw query in ASP.NET core to return the row count from a table?
Currently, I am doing this and the returned result is -1. I think the return result is based on the number of records affected.
int numberOfRows = await
appDbContext.Database.ExecuteSqlInterpolatedAsync(
$"SELECT CODE FROM [samaster] WHERE CODE={productBrandCode} AND WAREHOUSE={warehouse} ");
Any idea on how to get the count back to numberOfRows variable will be appreciated.
NOTE: The above table is not a model so I need to run a raw query.
Thanks
It is currently not possible to get the query result when using ExecuteSqlInterpolatedAsync. The same applies to any additional LINQ Statements.
You can, however, use the underlying ADO.net Provider:
public IList<IDictionary<string, dynamic>> SelectDynamic(string table)
{
using (var command = Database.GetDbConnection().CreateCommand())
{
command.CommandText = $"SELECT * FROM [{table}]";
command.CommandType = CommandType.Text;
Database.OpenConnection();
using (var result = command.ExecuteReader())
{
var entities = new List<IDictionary<string, dynamic>>();
while (result.Read())
{
var dict = new Dictionary<string, dynamic>();
for (int i = 0; i < result.FieldCount; i++)
{
dict.Add(result.GetName(i), result.GetValue(i));
}
entities.Add(dict);
}
return entities;
}
}
}
Add this to your DbContext Class and Call it with:
using (var context = new MyDbContext()) // Or get it with DI, depends on your application
{
var count = context.SelectDynamic("samaster").Where(d => d["CODE"] == productBrandCode && d["WAREHOUSE"] == warehouse).Count();
}
Beware, however, that this is an expensive operation if you have a lot of rows in your table!
An alternative approach to only fetch the relevant results would be to replace
command.CommandText = $"SELECT * FROM [{table}]";
with
command.CommandText = $"SELECT CODE FROM [samaster] WHERE CODE={productBrandCode} AND WAREHOUSE={warehouse}";
and pass the parameters as function parameters.
public IList<IDictionary<string, dynamic>> SelectDynamic(string productBrandCode, string warehouse)
{...
Also make sure to escape all parameters if they are in any way submitted by user input to prevent SQL Injection Attacks!
There are two common approaches :
A.
int numberOfRows = await appDbContext.Database.ExecuteSqlInterpolatedAsync($"SELECT CODE FROM [samaster] WHERE CODE={productBrandCode} AND WAREHOUSE={warehouse} ").Count();
B.
int numberOfRows = await appDbContext.Database.ExecuteSqlInterpolatedAsync($"SELECT count(*) FROM [samaster] WHERE CODE={productBrandCode} AND WAREHOUSE={warehouse} ").First();
Since nobody gave me the correct answer. I end up using the following.
public async Task<bool> IsAValidProduct(string productBrandCode)
{
int count = 0;
await using DbCommand command = appDbContext.Database.GetDbConnection().CreateCommand();
command.CommandText =
"SELECT COUNT(CODE) FROM [samaster] WHERE CODE=#productBrandCode AND WAREHOUSE=#warehouse ";
command.CommandType = CommandType.Text;
command.Parameters.Add(new SqlParameter("#productBrandCode", SqlDbType.VarChar)
{Value = productBrandCode});
command.Parameters.Add(new SqlParameter("#warehouse", SqlDbType.VarChar)
{Value = warehouse});
await appDbContext.Database.OpenConnectionAsync();
count = (int) await command.ExecuteScalarAsync();
await appDbContext.Database.CloseConnectionAsync();
return count == 1;
}
int c= dbObj.Database.ExecuteSqlRaw(sql); //User this code
I have a Real Estate site with a Properties table and a PropertyImages table.
When the user uploads a picture, I want to run a query in the PropertyImages. then append that number to the picturename.
public IQueryable GetPictureCount()
{
int propertyId = Convert.ToInt16(ddlSelectProperty.SelectedValue);
var _db = new RESolution.Models.PropertyContext();
IQueryable query = _db.PropertyImages;
var mypic = (from c in _db.PropertyImages
where c.PropertyID == propertyId
select c).FirstOrDefault();
lblCount.Text = Convert.ToString(query);
}
I get this error: "Not all code paths return a value"
My environment is as follows:
VS Express 2013
Sql Express
win 8.1 development computer
when I change IQuaryable to "void" I get the error
'lblCount.text = query.Count().ToString();'
System.Linq.IQueryable does not contain a definition for 'Count' I have looked for a using directive but found non
I am still a little confused, though you guys are a god send.
Here is where I was able to get to work.
public void GetPictureCount()
{
lblfnameCheck.Text = ddlSelectProperty.SelectedValue;
int propertyId = Convert.ToInt32(lblfnameCheck.Text);
var _db = new RESolution.Models.PropertyContext();
var count = _db.PropertyImages.Count(t => t.PropertyID == propertyId);
lblCount.Text = Convert.ToString(count);
}
As dasblinkenlight mentioned, it seems that you do not want to return something, so make the method a void.
You probable want something like this:
public void GetPictureCount()
{
int propertyId = Convert.ToInt16(ddlSelectProperty.SelectedValue);
var _db = new RESolution.Models.PropertyContext();
IQueryable query = _db.PropertyImages;
var mypic = (from c in _db.PropertyImages
where c.PropertyID == propertyId
select c).FirstOrDefault();
var nrOfPics = Convert.ToString(query.Count());
lblCount.Text = nrOfPics;
mypic.name = mypic.name + nrOfPics; // I am guessing the name property
_db.SaveChanges();
}
I have recently had performance problem with Nhibernate generated SQL as described in
Nhibernate generate plain sql query instead of execution statement
I also found a link describing similar experience from the one and only Jeff Atwood at
http://legeronline.blogspot.ca/2009/03/evils-of-slow-paramaterized-query-plans.html
Does anyone know if there is anyway to add an "Optimize Uknown" option to Nhibernate?
This example is a little more verbose:
public class OptionInterceptor: EmptyInterceptor
{
public override SqlString OnPrepareStatement(SqlString sql)
{
var parameters = sql.GetParameters();
var paramCount = parameters.Count();
if (paramCount == 0)
return sql;
string optionString = " OPTION (OPTIMIZE FOR (";
for (var i = 0; i < paramCount; i++)
{
var comma = i > 0 ? "," : string.Empty;
optionString = optionString + comma + "#p" + i + " UNKNOWN";
}
optionString = optionString + "))";
var builder = new SqlStringBuilder(sql);
builder.Add(optionString);
return builder.ToSqlString();
}
}
Then in your sessionfactory/initializer:
configuration.ExposeConfiguration(x =>
{
x.SetInterceptor(new OptionInterceptor());
});
you could look into extending the MsSqlDialect or
implement IConnectionProvider to inject your own commandwrapper which adds the hint when ExecuteReader() is called
I tend to use something like this:
return NHibernateSession.Current.CreateSQLQuery
(
#"
some sql
"
)
.SetResultTransformer(NHibernate.Transform.Transformers.AliasToBean(typeof(someviewmodel)))
.List<someviewmodel>();
to map my sql output to a viewmodel. Is it quite straightforward to achieve the same mapping to a Dictionary whilst using CreateSQLQuery which spews out two int columns?
Thanks.
You would basically need to create your own transformer and specify it in your SetResultTransformer call.
It might look something like this:
public class CustomDictionaryTransformer : IResultTransformer
{
public object TransformTuple(object[] tuple, string[] aliases)
{
KeyValuePair<int, int> result = new KeyValuePair<int, int>();
for (int i = 0; i < tuple.Length; i++)
{
string alias = aliases[i];
var val = new KeyValuePair<int, int>();
if (alias == "key") result.Key = (int)tuple[i];
else result.Value = (int)tuple[i];
}
return result;
}
public IList TransformList(IList collection)
{
return collection;
}
}
I have a list of Ids and I want to get all the rows back in one query. As a list of objects(So a List of Products or whatever).
I tried
public List<TableA> MyMethod(List<string> keys)
{
var query = "SELECT * FROM TableA WHERE Keys IN (:keys)";
var a = session.CreateQuery(query).SetParameter("keys", keys).List();
return a; // a is a IList but not of TableA. So what do I do now?
}
but I can't figure out how to return it as a list of objects. Is this the right way?
List<TableA> result = session.CreateQuery(query)
.SetParameterList("keys", keys)
.List<TableA>();
Howeever there could be a limitation in this query if number of ":keys" exceed more than 1000 (incase of oracle not sure with other dbs) so i would recommend to use ICriteria instead of CreateQuery- native sqls.
Do something like this,
[TestFixture]
public class ThousandIdsNHibernateQuery
{
[Test]
public void TestThousandIdsNHibernateQuery()
{
//Keys contains 1000 ids not included here.
var keys = new List<decimal>();
using (ISession session = new Session())
{
var tableCirt = session.CreateCriteria(typeof(TableA));
if (keys.Count > 1000)
{
var listsList = new List<List<decimal>>();
//Get first 1000.
var first1000List = keys.GetRange(0, 1000);
//Split next keys into 1000 chuncks.
for (int i = 1000; i < keys.Count; i++)
{
if ((i + 1)%1000 == 0)
{
var newList = new List<decimal>();
newList.AddRange(keys.GetRange(i - 999, 1000));
listsList.Add(newList);
}
}
ICriterion firstExp = Expression.In("Key", first1000List);
ICriterion postExp = null;
foreach (var list in listsList)
{
postExp = Expression.In("Key", list);
tableCirt.Add(Expression.Or(firstExp, postExp));
firstExp = postExp;
}
tableCirt.Add(postExp);
}
else
{
tableCirt.Add(Expression.In("key", keys));
}
var results = tableCirt.List<TableA>();
}
}
}