I have a simple GET method, which returns IQueryable, and has some preconditions on query:
[Queryable(HandleNullPropagation = HandleNullPropagationOption.False)]
public IQueryable<Message> Get()
{
using (var session = RavenStore.GetSession())
{
var messages = session.Query<Message>().Where(x => x.TargetUserId == this.User.Identity.Name || x.SourceUserId == this.User.Identity.Name);
return messages;
}
}
This is RavenDB, btw. The issue I'm having is that upon execution the user id is replaced with "[EMPTY_STRING]", so the actual query its running is this:
'TargetUserId:[[EMPTY_STRING]] OR SourceUserId:[[EMPTY_STRING]]' on
index .....
which is obviously wrong.
If I'm returning List instead of IQueriable - it works fine, so something later in the pipeline changes the query. Does anyone have any insight on how to make this work ?
It should work when the values are copied to a local variable first:
var userName = this.User.Identity.Name;
return session.Query<Message>()
.Where(x => x.TargetUserId == userName ||
x.SourceUserId == userName);
This is because by the time the query is executed, the Raven Client query translator can't resolve the objects expressed in the predicate. By copying them into a local variable, you are passing a constant value into the expression.
I believe this is related to closures. Perhaps someone with more direct knowledge of expression trees can explain better in comments.
Related
I have a field in my database with duplicates. I want to use it in a dropdown list, which has to return distinct data.
Here is the method that I created to do this:
public IEnumerable<SelectListItem> GetBranches(string username)
{
using (var objData = new BranchEntities())
{
IEnumerable<SelectListItem> objdataresult = objData.ABC_USER.Select(c => new SelectListItem
{
Value = c.BRANCH_CODE.ToString(),
Text = c.BRANCH_CODE
}).Distinct(new Reuseablecomp.SelectListItemComparer());
return objdataresult;
}
}
Here is the class I am using:
public static class Reuseablecomp
{
public class SelectListItemComparer : IEqualityComparer<SelectListItem>
{
public bool Equals(SelectListItem x, SelectListItem y)
{
return x.Text == y.Text && x.Value == y.Value;
}
public int GetHashCode(SelectListItem item)
{
int hashText = item.Text == null ? 0 : item.Text.GetHashCode();
int hashValue = item.Value == null ? 0 : item.Value.GetHashCode();
return hashText ^ hashValue;
}
}
}
Nothing is returned and I get the error below. When I try a basic query without Distinct, everything works fine.
{"The operation cannot be completed because the DbContext has been disposed."}
System.Exception {System.InvalidOperationException}
Inner exception = null
How can I return distinct data for my dropdown?
Technically, your problem can be solved simply by appending .ToList() after your Distinct(...) call. The problem is that queries are evaluated JIT (just in time). In other words, until the actual data the query represents is needed, the query is not actually sent to the database. Calling ToList is one such thing that requires the actual data, and therefore will cause the query to be evaluated immediately.
However, the root cause of your problem is that you are doing this within a using statement. When the method exits, the query has not yet been evaluated, but you have now disposed of your context. Therefore, when it comes time to actually evaluate that query, there's no context to do it with and you get that exception. You should really never use a database context in conjuction with using. It's just a recipe for disaster. Your context should ideally be request-scoped and you should use dependency injection to feed it to whatever objects or methods need it.
Also, for what it's worth, you can simply move your Distinct call to before your Select and you won't need a custom IEqualityComparer any more. For example:
var objdataresult = objData.ABC_USER.Distinct().Select(c => new SelectListItem
{
Value = c.BRANCH_CODE.ToString(),
Text = c.BRANCH_CODE
});
Order of ops does matter here. Calling Distinct first includes it as part of the query to the database, but calling it after, as you're doing, runs it on the in-memory collection, once evaluated. The latter requires, then, custom logic to determine what constitutes distinct items in an IEnumerable<SelectListItem>, which is obviously not necessary for the database query version.
I am using criteria to query the database based on the unique key. But I am coming through a weird scenario. After two or three queries, it starts giving me timeout expired error.
using (NHibernate.ISession session = m_SessionFactory.OpenSession())
{
using (ITransaction transacion = session.BeginTransaction())
{
if (cashActivity.ActivityState == ApplicationConstants.TaxLotState.Deleted || cashActivity.ActivityState == ApplicationConstants.TaxLotState.Updated)
{
IList<CashActivity> lsCActivity = RetrieveEquals<CashActivity>("UniqueKey",cashActivity.UniqueKey);
if (lsCActivity != null && lsCActivity.Count > 0)
cashActivity.CashActivityID = lsCActivity[0].CashActivityID;
}
if (cashActivity.ActivityState == ApplicationConstants.TaxLotState.Deleted)
{
session.Delete(cashActivity);
}
else
session.SaveOrUpdate(cashActivity);
}
}
}
public IList<T> RetrieveEquals<T>(string propertyName, object propertyValue)
{
using (Isession session = m_SessionFactory.OpenSession())
{
Icriteria criteria = session.CreateCriteria(typeof(T));
criteria.Add(Restrictions.Eq(propertyName, PropertyValue));
IList<T> matchingObjects = criteria.List<T>();
return matchingObjects;
}
}
I made changes in the code and start using StateLess Session but that change only reduces the frequency of timeout error.
After decugging , I found IList matchingObjects = criteria.List(); is cause of the exception. But this is only returning only one value, so it should not result timeout error since table also doesnt contain more than 100 rows as of now. Any Suggestions??
Unless you have wrapped NHibernate's ISessionFactory in something else, each call to OpenSession() will yield a new session. So the above code involves multiple sessions and it isn't clear if this is required.
Theoretically, a query on the session in RetrieveEquals() could block because of locks taken on the connection used in the calling method. But given the code as shown I can't see anything to prove this.
The calling method first updates a property of cashActivity, then in some cases goes on to delete the object. And there is no Commit(). This seems strange - is this really the used code or might there be a copy/paste error?
You also say "after two or three queries"... do you imply that there is a loop somewhere which isn't shown?
Given 1000 documents with a complex data structure. for e.g. a Car class that has three properties, Make and Model and one Id property.
What is the most efficient way in C# to push these documents to raven db (preferably in a batch) without having to query the raven collection individually to find which to update and which to insert. At the moment I have to going like so. Which is totally inefficient.
note : _session is a wrapper on the IDocumentSession where Commit calls SaveChanges and Add calls Store.
private void PublishSalesToRaven(IEnumerable<Sale> sales)
{
var page = 0;
const int total = 30;
do
{
var paged = sales.Skip(page*total).Take(total);
if (!paged.Any()) return;
foreach (var sale in paged)
{
var current = sale;
var existing = _session.Query<Sale>().FirstOrDefault(s => s.Id == current.Id);
if (existing != null)
existing = current;
else
_session.Add(current);
}
_session.Commit();
page++;
} while (true);
}
Your session code doesn't seem to track with the RavenDB api (we don't have Add or Commit).
Here is how you do this in RavenDB
private void PublishSalesToRaven(IEnumerable<Sale> sales)
{
sales.ForEach(session.Store);
session.SaveChanges();
}
Your code sample doesn't work at all. The main problem is that you cannot just switch out the references and expect RavenDB to recognize that:
if (existing != null)
existing = current;
Instead you have to update each property one-by-one:
existing.Model = current.Model;
existing.Make = current.Model;
This is the way you can facilitate change-tracking in RavenDB and many other frameworks (e.g. NHibernate). If you want to avoid writing this uinteresting piece of code I recommend to use AutoMapper:
existing = Mapper.Map<Sale>(current, existing);
Another problem with your code is that you use Session.Query where you should use Session.Load. Remember: If you query for a document by its id, you will always want to use Load!
The main difference is that one uses the local cache and the other not (the same applies to the equivalent NHibernate methods).
Ok, so now I can answer your question:
If I understand you correctly you want to save a bunch of Sale-instances to your database while they should either be added if they didn't exist or updated if they existed. Right?
One way is to correct your sample code with the hints above and let it work. However that will issue one unnecessary request (Session.Load(existingId)) for each iteration. You can easily avoid that if you setup an index that selects all the Ids of all documents inside your Sales-collection. Before you then loop through your items you can load all the existing Ids.
However, I would like to know what you actually want to do. What is your domain/use-case?
This is what works for me right now. Note: The InjectFrom method comes from Omu.ValueInjecter (nuget package)
private void PublishSalesToRaven(IEnumerable<Sale> sales)
{
var ids = sales.Select(i => i.Id);
var existingSales = _ravenSession.Load<Sale>(ids);
existingSales.ForEach(s => s.InjectFrom(sales.Single(i => i.Id == s.Id)));
var existingIds = existingSales.Select(i => i.Id);
var nonExistingSales = sales.Where(i => !existingIds.Any(x => x == i.Id));
nonExistingSales.ForEach(i => _ravenSession.Store(i));
_ravenSession.SaveChanges();
}
I want to pass a parameter in my method call, if set (its a boolean), then return a Stateless session.
I don't want to duplicate the QueryOver code, is there a way to have it like:
public virtual IList<User> GetAllUsers(bool isStateless)
{
var query = QueryOver<User>().Where(x => x.UserType == 1).ToList();
if(isStateless)
return NHibernateHelper.Session(query);
else
return NHibernateHelper.StatelessSession(query);
}
I know the above won't work, but I hope it is clear what I am after.
The only way I know is to basically duplicate the entire queryover code, and the only different between the code blocks will be that one will use .Session and the other will use .StatelessSession.
Hoping there is a cleaner way.
var query = QueryOver.Of<User>().Where(x => x.UserType == 1);
IQueryOver<User, User> executableQuery;
if(isStateless)
executableQuery = query.GetExecutableQueryOver(NHibernateHelper.Session);
else
executableQuery = query.GetExecutableQueryOver(NHibernateHelper.StatelessSession);
return executableQuery.ToList();
I'm new to NHibernate and was assigned to a task where I have to change a value of an entity property and then compare if this new value (cached) is different from the actual value stored on the DB. However, every attempt to retrieve this value from the DB resulted in the cached value. As I said, I'm new to NHibernate, maybe this is something easy to do and obviously could be done with plain ADO.NET, but the client demands that we use NHibernate for every access to the DB. In order to make things clearer, those were my "successful" attempts (ie, no errors):
1
DetachedCriteria criteria = DetachedCriteria.For<User>()
.SetProjection(Projections.Distinct(Projections.Property(UserField.JobLoad)))
.Add(Expression.Eq(UserField.Id, userid));
return GetByDetachedCriteria(criteria)[0].Id; //this is the value I want
2
var JobLoadId = DetachedCriteria.For<User>()
.SetProjection(Projections.Distinct(Projections.Property(UserField.JobLoad)))
.Add(Expression.Eq(UserField.Id, userid));
ICriteria criteria = JobLoadId.GetExecutableCriteria(NHibernateSession);
var ids = criteria.List();
return ((JobLoad)ids[0]).Id;
Hope I made myself clear, sometimes is hard to explain a problem when even you don't quite understand the underlying framework.
Edit: Of course, this is a method body.
Edit 2: I found out that it doesn't work properly for the method call is inside a transaction context. If I remove the transaction, it works fine, but I need it to be in this context.
I do that opening a new stateless session for geting the actual object in the database:
User databaseuser;
using (IStatelessSession session = SessionFactory.OpenStatelessSession())
{
databaseuser = db.get<User>("id");
}
//do your checks
Within a session, NHibernate will return the same object from its Level-1 Cache (aka Identity Map). If you need to see the current value in the database, you can open a new session and load the object in that session.
I would do it like this:
public class MyObject : Entity
{
private readonly string myField;
public string MyProperty
{
get { return myField; }
set
{
if (value != myField)
{
myField = value;
DoWhateverYouNeedToDoWhenItIsChanged();
}
}
}
}
googles nhforge
http://nhibernate.info/doc/howto/various/finding-dirty-properties-in-nhibernate.html
This may be able to help you.