Check if object exists in database without loading object with NHibernate - nhibernate

Is there a way in NHibernate to check if an object exists in the database without having to get/load the object?

You could use one of the following 3 queries (or you could use Criteria API Projections.RowCountInt64() from David answer):
bool exist = session.Query<Employee>()
.Any(x => x.EmployeeID == 1);
bool exist = session.QueryOver<Employee>()
.Where(x => x.EmployeeID == 1)
.RowCount() > 0;
bool exist = session.Query<Employee>()
.Count(x => x.EmployeeID == 1) > 0;
Just keep in mind that Any is worst of those three because it fetches entity. Here is sql query generated for each:
exec sp_executesql N'select TOP (1) employee0_.EmployeeID as EmployeeID0_, employee0_.Name as Name0_ from Employee employee0_ where employee0_.EmployeeID=#p0',N'#p0 int',#p0=1
exec sp_executesql N'SELECT count(*) as y0_ FROM Employee this_ WHERE this_.EmployeeID = #p0',N'#p0 int',#p0=1
exec sp_executesql N'select cast(count(*) as INT) as col_0_0_ from Employee employee0_ where employee0_.EmployeeID=#p0',N'#p0 int',#p0=1

So I let myself to do some tests with your examples #Jamie Ide #Darius Kucinskas #Dmitry
So:
var exists = session
.CreateQuery("select 1 from Widget where _color = 'green'")
.SetMaxResults(1)
.UniqueResult<Int32?>()
.HasValue;
in my case was 18% faster than
bool exist = session.Query<Employee>()
.Any(x => x.EmployeeID == 1);
14% than
bool exist = session.Query<Employee>()
.Count(x => x.EmployeeID == 1) > 0;
and 8%
bool exist = session.QueryOver<Employee>()
.Where(x => x.EmployeeID == 1)
.RowCount() > 0;
So in my opinion even if hard coded query is fastest the
bool exist = session.QueryOver<Employee>()
.Where(x => x.EmployeeID == 1)
.RowCount() > 0;
is best option because of good habits and code clearness

Could always do a count.
I tend to use DetachedCriteria, so I'd have something like:
var criteria = // some criteria that will identify your object
var result = criteria
.GetExecutableCriteria(Session)
.SetProjection(Projections.RowCountInt64())
.UniqueResult();
return result > 0;

To expand on Darius Kucinskas' excellent answer, you can avoid fetching the entity using Select:
bool exist = session.Query<Employee>()
.Where(x => x.EmployeeID == 1)
.Select(x => x.EmployeeID)
.Any();
As mentioned, the query performance should be the same however I would expect this to reduce network traffic.

I think you are looking for this...
var fooExists = session.Query<Foo>().Any(f => /*condition*/);

var exists = 1 == session.CreateQuery("select 1 from MyEntity where Property = :value")
.SetValue("value", xxx)
.UniqueResult<Int32?>();

Based on Ricardo answer, this seems like the most efficient way to check if object exists using HQL. It doesn't COUNT and doesn't load the object unnecessarily:
var exists = session
.CreateQuery("select 1 from Widget where _color = 'green'")
.SetMaxResults(1)
.UniqueResult<Int32?>()
.HasValue;
It generates this SQL (note that this is SQLite, so LIMIT instead of TOP)
select
1 as col_0_0_
from
Widgets
where
Color='green' limit 1;

You can have a try:
public virtual T FindById<T>(int id)
{
return session.Get(typeof(T), id));
}

I created an extension method from the answers above. I choose to use the Any version but with a Select on the primary key the same amount of data will be returned depend on the primary key type.
public static bool Exists<TModel, TKey>(
this IQueryable<TModel> query, Expression<Func<TModel, TKey>> selector, TKey id)
where TKey : class
{
return query
.Select(selector)
.Any(x => x == id);
}
public static Task<bool> ExistsAsync<TModel, TKey>(
this IQueryable<TModel> query, Expression<Func<TModel, TKey>> selector, TKey id)
where TKey : class
{
return query
.Select(selector)
.AnyAsync(x => x == id);
}

Related

Easiest way to check record is exist or not in Linq to Entity Query

in store procedure we can check record is exist or not using following query for fast performance
if EXISTS ( Select 1 from Table_Name where id=#id )
But what about Linq query.
right now i have to store whole data in object like this
UserDetail _U=db.UserDetails.where(x=>x.id==1).FirstOrDefault();
Any Solution?
Use Linq's Any ie bool exist = db.UserDetails.Any(x => x.id == 1);
if(db.UserDetails.Any(x => x.id == 1)) {
var userDetail = db.UserDetails.FirstOrDefault(x => x.id == 1);
}
bool exist = db.UserDetails.Where(x=>x.id==1).Any();
if(exist){
//your-code
}else{
//your-code
}
Just check
if(_U == null)
This way you will get what you want in single query and you not need to execute addition query like
db.UserDetails.Any(x => x.id == 1)
I think, it does not require to fire two query. It can be accomplish by single query.
UserDetails objUserDetails =db.UserDetails.FirstOrDefault(x => x.id == 1);
if(objUserDetails==null)
{
// Do something
}
else
{
// do something with objUserDetails
}
var qry = db.User_Detail.Where(x => x.User_Id == 1).FirstOrDefault();
if(qry !=null)
{
// Do something
}
else
{
return false;
}
Try This...

Raven DB filter on subset of array items and sort on the cheapest of the filter results items

Assuming i have an parent class that I filter on various properties, one of which is a property that is an array of items .
Now say that i want to only return the parent item if my array of items as above a min value and below a max value ...that's fine i can work that bit out;
What if i then want to then sort on the filtered result set of those items
I made a c# fiddle example to show what im trying to achieve :
https://dotnetfiddle.net/mV4d28
(note that foo2 is returned first even though foo1 has items in its array that are less that those in foo2)
As i need to do this using a index i need the index to be able to compute the order by based on the filter criteria used in my query.
I know elasticsearch has an inner hits function that dose this and mongo has pipelines which also dose this so im sure Raven must have a way of doing this too ?
I was hoping using just index and a transform with prams i could achieve this so I tried it:
my index and transform look like this
public class familyTransfrom : AbstractTransformerCreationTask<ParentItem>
{
public class Result : ParentItem{
public double[] ChildItemValuesFiltered { get; set; }
}
public familyTransfrom(){
TransformResults = parents => from parent in parents
let filterMinValue = Convert.ToDouble(ParameterOrDefault("FilterMinValue", Convert.ToDouble(0)).Value<double>())
let filterMaxValue = Convert.ToDouble(ParameterOrDefault("FilterMaxValue", Convert.ToDouble(9999)).Value<double>())
select new Result{
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
//ChildItemValuesFiltered = parent.ChildItems.Where(p => p.ChildItemValues.Any(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).SelectMany(t => t.ChildItemValues).ToArray<double>(),
ChildItemValuesFiltered = parent.ChildItems.SelectMany(p => p.ChildItemValues.Where(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).ToArray<double>(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
};
}
}
public class familyIndex : AbstractIndexCreationTask<ParentItem>{
public class Result : ParentItem {
public double[] ChildItemValues { get; set; }
}
public familyIndex(){
Map = parents => from parent in parents
select new Result{
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
ChildItemValues = parent.ChildItems.SelectMany(p => p.ChildItemValues.Select(y => y)).ToArray(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
};
Index("ParentItemId", FieldIndexing.Analyzed);
Index("ParentItemName", FieldIndexing.Analyzed);
Index("ParentItemValue", FieldIndexing.Analyzed);
Index("ChildItemValues", FieldIndexing.Analyzed);
Index("ChildItems", FieldIndexing.Analyzed);
}
}
my query is as follows , (this is using the live raven playground so this should just work out of the box it you want to use it)
using (IDocumentStore store = new DocumentStore { Url = "http://live-test.ravendb.net/", DefaultDatabase = "altha" })
{
store.Initialize();
using (IDocumentSession session = store.OpenSession())
{
if(1 == 2){
//foreach (ParentItem element in data.OfType<ParentItem>()) {
// session.Store((ParentItem)element);
// session.SaveChanges();
//}
new familyIndex().Execute(store);
new familyTransfrom().Execute(store);
}else{
double filterMinValue = 3.0;
double filterMaxValue = 4.0;
var results = session
.Advanced
.DocumentQuery<familyIndex.Result,familyIndex>()
.WhereBetweenOrEqual("ChildItemValues", filterMinValue, filterMaxValue)
.SetResultTransformer<familyTransfrom, familyTransfrom.Result>()
.SetTransformerParameters(new Dictionary<string, RavenJToken> {
{ "FilterMinValue", filterMinValue },
{ "FilterMaxValue", filterMaxValue } })
.OrderBy("ChildItemValues")
.OfType<ParentItem>().ToList();
results.Dump();
}}
}
What i found was i cant use "ChildItemValuesFiltered" from the transform result as its not index. So unless i can order by the result of a transform ? i couldn't get this to work as although it filters it dosnt order correctly.
Is there another to achieve what i want using projections or intersection or rank or reduce try method ?
I was thinking if i had to perhaps i could use https://ravendb.net/docs/article-page/3.5/csharp/indexes/querying/sorting#custom-sorting
and do something like this:
public class SortByNumberOfCharactersFromEnd : IndexEntriesToComparablesGenerator
{
private readonly double filterMinValue;
private readonly double filterMinValue;
public SortByNumberOfCharactersFromEnd(IndexQuery indexQuery)
: base(indexQuery)
{
filterMinValue = IndexQuery.TransformerParameters["FilterMinValue"].Value<double>(); // using transformer parameters to pass the length explicitly
filterMaxValue = IndexQuery.TransformerParameters["FilterMaxValue"].Value<double>();
}
public override IComparable Generate(IndexReader reader, int doc)
{
var document = reader.Document(doc);
double[] childItemValues = (double[])document.GetValues("ChildItemValuesFiltered").Select(double.Parse).ToArray(); // this field is stored in index
return childItemValues.Where(x => x >= min && x <= max).Min();
}
}
then do a where filter and order by clause using index and transform passing in the same prams that i use in the where filter . however im not sure if this would work ?
More importantly im not sure how i go about getting the sort dll into the plugins ie what name space should the class go under, what name spaces dose it need to import, what assembly name dose it need to use etc
According to https://ravendb.net/docs/article-page/3.5/csharp/server/plugins/what-are-plugins i just need to drop the dll in and raven will this this up , however i cant seem to find what name space i need to reference for IndexEntriesToComparablesGenerator ?
im using linqpad 5 to test my stuff ...so in order to use the custom order i have to reference the class
any tips or advice or how to guild/examples welcome
so it didn't occur to me that i could do the filtering in the transform
TransformResults = parents => from parent in parents
let filterMinValue = Convert.ToDouble(ParameterOrDefault("FilterMinValue", Convert.ToDouble(0)).Value<double>())
let filterMaxValue = Convert.ToDouble(ParameterOrDefault("FilterMaxValue", Convert.ToDouble(9999)).Value<double>())
select new {
ParentItemId = parent.ParentItemId,
ParentItemName = parent.ParentItemName,
ParentItemValue = parent.ParentItemValue,
//ChildItemValuesFiltered = parent.ChildItems.Where(p => p.ChildItemValues.Any(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).SelectMany(t => t.ChildItemValues).ToArray<double>(),
ChildItemValuesFiltered = parent.ChildItems.SelectMany(p => p.ChildItemValues.Where(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).ToArray<double>(),
ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()
} into r
where r.ChildItemValuesFiltered.Length > 0
orderby r.ChildItemValuesFiltered.Min()
select r;
This gives me what i wanted, here are the sample query:
http://live-test.ravendb.net/databases/altha/indexes/familyIndex?start=0&pageSize=25&resultsTransformer=familyTransfrom&tp-FilterMinValue=3&tp-FilterMaxValue=4
i cant take credit for this as guys at raven helped me but sharing the knowledge for others

NHibernate named query and multiple result sets

We have a stored procedure that returns several tables. When calling it using NHibernate, we use the bean transformer but only get the first table transformed and all other results are ignored.
I know that NH is able to process several queries in one db trip using futures but we only have one query and it produces a result that is similar to what we would get with futures, but getting this from a stored procedure.
I believe this scenario is quite common but could not find any clues. Is it possible to use NH to retrieve such results?
Yes,you can use MultiQuery "Hack" like this:
The procudure:
CREATE PROCEDURE [dbo].[proc_Name]
AS BEGIN
SELECT * FROM Question
SELECT * FROM Question
END
The NHibernate Query Code:
public void ProcdureMultiTableQuery()
{
var session = Session;
var procSQLQuery = session.CreateSQLQuery("exec [proc_Name] ?,?");// prcodure returns two table
procSQLQuery.SetParameter(0, userId);
procSQLQuery.SetParameter(1, page);
procSQLQuery.AddEntity(typeof(Question));
var multiResults = session.CreateMultiQuery()
.Add(procSQLQuery)
// More table your procedure returns,more empty SQL query you should add
.Add(session.CreateSQLQuery(" ").AddEntity(typeof(Question))) // the second table returns Question Model
.List();
if (multiResults == null || multiResults.Count == 0)
{
return;
}
if (multiResults.Count != 2)
{
return;
}
var questions1 = ConvertObjectsToArray<Question>((System.Collections.IList)multiResults[0]);
var questions2 = ConvertObjectsToArray<Question>((System.Collections.IList)multiResults[1]);
}
static T[] ConvertObjectsToArray<T>(System.Collections.IList objects)
{
if (objects == null || objects.Count == 0)
{
return null;
}
var array = new T[objects.Count];
for (int i = 0; i < array.Length; i++)
{
array[i] = (T)objects[i];
}
return array;
}

Nhibernate Queryover with subquery get Next free number

How can I do this in nHibernate using queryover :
SELECT MIN(t.subid)+1 AS NextID
FROM subject t
WHERE NOT EXISTS
(SELECT id FROM subject n WHERE n.subid=t.subid+1)
Currently I have this but its not working because of this statement "SubId+1"
_session.QueryOver(() => subject)
.WithSubquery
.WhereNotExists(
subject
.Where(x => x.SubId==SubId+1)
.Select(x => x.Id)
)
.Select(Projections.ProjectionList()
.Add(Projections.Min<subject>(x => x.SubId)))
.List().First()
One way, using NOT IN instead of NOT EXISTS (results are the same) would be like this (the SQL query would be a bit different, but result will be the same)
Subjectsubject = null;
Subjectinner = null;
var subquery = QueryOver.Of<Subject>(() => inner)
.Select(Projections.SqlProjection(
// important the subid is the column name, not property name
" subid - 1 as innerId" // the trick here is, that we compare
, new[] { "innerId" } // inner ID - 1, with outer ID
, new IType[] { NHibernateUtil.Int32 }))
;
var id = session.QueryOver(() => subject)
.WithSubquery
.WhereProperty(() => subject.ID)
.NotIn(subquery)
.Select(Projections.ProjectionList().Add(Projections.Min<Subject>(s => s.ID)))
.SingleOrDefault<int>();
var id = id + 1 ; // here we increment in C#, instead of projecting that into SQL
Summary: not sure about algorithm you've used, but the trick how to get "subid - 1" is to use projection:
Projections.SqlProjection(
" subid - 1 as innerId" // sql statement
, new[] { "innerId" } // its alias
, new IType[] { NHibernateUtil.Int32 }) // type is int
NOTE: I would expect the last Projections to be MAX

How to select from a subselect on NHibernate

How can I map this SQL using NHibernate Criteria API?
Sql:
SELECT COUNT(*) FROM (
SELECT FirstName, LastName FROM Employees GROUP BY FirstName, LastName
) AS Query
This is a very very simple query, my query has a SubSelect much more complex.
So, any idea?
I found a solution for the question, it's a very big hack but it works as expected.
I had to get the generated SQL and surround it with the SELECT COUNT(*) query. Here is the code to do that:
public ISQLQuery BuildCountQuery(ICriteria criteria)
{
CriteriaImpl c = (CriteriaImpl)criteria;
SessionImpl s = (SessionImpl)c.Session;
string entityOrClassName = ExtractRealClassName(c);
SessionFactoryImpl factory = (SessionFactoryImpl)s.SessionFactory;
String[] implementors = factory.GetImplementors(entityOrClassName);
string implementor = implementors.Length == 0 ? null : implementors[0];
var persister = (IOuterJoinLoadable)factory.GetEntityPersister(implementor);
CriteriaLoader loader = new CriteriaLoader(persister, factory, c, implementor, s.EnabledFilters);
SqlString sql = loader.SqlString.Insert(0, "SELECT COUNT(*) FROM (");
sql = sql.Append(") AS Query");
var parameters = loader.Translator.CollectedParameters;
var sqlQuery = this.session.CreateSQLQuery(sql.ToString());
for (int i = 0; i < parameters.Count; i++)
sqlQuery.SetParameter(i, parameters.ElementAt(i).Value, parameters.ElementAt(i).Type);
return sqlQuery;
}
private string ExtractRealClassName(CriteriaImpl criteria)
{
Type rootEntityType = criteria.GetRootEntityTypeIfAvailable();
if (rootEntityType.GetInterfaces().Contains(typeof(INHibernateProxy)))
return criteria.GetRootEntityTypeIfAvailable().BaseType.FullName;
else
return criteria.EntityOrClassName;
}
DetachedCriteria criteriaEmployees = DetachedCriteria.For<Employees>();
criteriaEmployees.SetProjection(Projections.CountDistinct("FirstName"));
ICriteria executableCriteria = criteriaEmployees.GetExecutableCriteria(Session);
int count = executableCriteria.UniqueResult<int>();
DetachedCriteria can be used to create subqueries. Some examples are in the documentation.