On a web-app I'm working on it usingNHibernate with ASP.NET-MVC, we were experiencing some major lag on page loads, so I was given the task to look into it and do some refactoring.
When I looked through the code, there were many calls like this:
public static IEnumerable<Client> GetAllClients()
{
return new EntityManager<Client>()
.GetAll<Client>()
.Where(x => x.IsDeleted == false || x.IsDeleted == null);
}
After some debugging and searching I found out that this actually performs 2 separate calls to the database, one to GetAll then it applies the Where and does a second call, or something like that. I found another question on SO which made me decide to use a session instead:
public static IEnumerable<Client> GetAllClients()
{
using (ISession session = NHibernateSessionFactoryManager.Factory.OpenSession())
{
return session.QueryOver<Client>()
.Where(x => x.IsDeleted == false || x.IsDeleted == null)
.List();
}
}
this was a lot nicer as it took the Where clause into consideration before going to the database. However, there are still methods that I see calling GetAll() but then applying another Where on top of that:
var Clients = GetAllClients().Where(n => n.Location == "someCity");
My guess is this first calls the GetAll() using the session, then it applies the new Where and goes back to the database again? Is this true?
is there a better/more elegant way to do this?
It's a very common problem when you over abstract the data access.
The List method call in GetAllClients method will cause all client records being loaded into memory from database.
I would suggest using one NHibernate ISession per request, instead of one ISession per method call. And make your methods returning IQueryable instead of IEnumerable. Something like this (NHibernate 3.0+):
using NHibernate;
using NHibernate.Linq;
public class EntityManager<T> {
private ISession _session;
public EntityManager(ISession session) {
_session = session;
}
public IQueryable<T> GetAllClients() {
return _session.Query<T>();
}
}
Then you can filter by country:
GetAllClients().Where(it => it.Location == "city").ToList();
Or doing projection:
var clientShortInfos = GetAllClients().Where(it => it.Location == "city")
.Select(it => new
{
Id = it.Id,
FullName = it.FullName
})
.ToList();
This allows you to retrieve only needed fields instead of all fields from database.
Here are some helpful posts:
http://ayende.com/blog/3955/repository-is-the-new-singleton
http://ayende.com/blog/4101/do-you-need-a-framework
It depends on what is the result type of GetAll of EntityManager. If it is IQueryable<Clinet> then the where will be applied before hitting the database, if it is IEnumerable<Client>, or list, or array then Where will be performed in memory.
Because GetAllClients returns IEnumerable<Client> the subsequent calls to Where will be performed in memory.
Also, in the second example you probably want to use Query (Linq) instead of QueryOver. Linq and QueryOver are different query engines.
Yes, You absolutely made correct solution. I don't know much about NHibernate but this case resembles with Entity Framework as well. Using session will avoid hit on server and prepare a query first. Once query is prepared then it will hit server.
You can also find an example of same where clause in NHibernate over here
Where clause in NHibernate
You can also find a good tutorial related to IQueryable concept in Nhibernate over here.
http://ayende.com/blog/2227/implementing-linq-for-nhibernate-a-how-to-guide-part-1
Related
In NHibernate, if an entity is lazy-loaded by one query, and then requested to be eager-loaded by a second query in the same session, the returned type in the second query is always a proxy (I believe a cached proxy from the first query). This can be demonstrated in this highly simplified and British example:
public class CarClassMap : ClassMap<Car>
{
public CarClassMap()
{
this.Id(x => x.Id);
this.Map(x => x.Model);
// note: lazy load by default
this.References(x => x.Colour).Column("ColourId").LazyLoad();
}
}
public class ColourClassMap : ClassMap<Colour>
{
public ColourClassMap()
{
this.Id(x => x.Id);
this.Map(x => x.Description);
}
}
public static void Main()
{
using (var session = CreateSessionFactory().OpenSession())
{
// note: both cars are the same colour in the database
var jaguar = session.QueryOver<Car>()
.Where(x => x.Id == 1)
.FutureValue()
.Value;
Console.WriteLine("Jaguar colour type=" + jaguar.Colour.GetType());
var aston =
session.QueryOver<Car>()
.Fetch(x => x.Colour).Eager //note: eager load
.Where(x => x.Id == 2)
.FutureValue()
.Value;
Console.WriteLine("Aston Martin colour type=" + aston.Colour.GetType());
}
Console.Read();
}
The output from this program is:
Jaguar colour type=ColourProxy
Aston Martin colour type=ColourProxy
Both 'Colour' properties are proxies, despite the second query requesting an eager load. However when running just the eager-load query:
public static void Main()
{
using (var session = CreateSessionFactory().OpenSession())
{
var aston =
session.QueryOver<Car>()
.Fetch(x => x.Colour).Eager
.Where(x => x.Id == 2)
.FutureValue()
.Value;
Console.WriteLine("Aston Martin colour type=" + aston.Colour.GetType());
}
Console.Read();
}
The output is:
Aston Martin colour type=TestApp.Colour
The concrete, underlying type.
In our real system, the returned object is passed to a mapping layer which performs some complex logic. This discrepancy is causing us issues because the property is typed differently depending on which queries have been previously issued in the Session.
Basically the question is how can we avoid a request for an eager-load resulting in a proxy, and rather force it to the concrete type? We know we can manually 'un-proxy' objects using NHibernate utilities but we would rather not have to do that every time this entity is queried. If possible we'd prefer a way to do it in the class map. Or is there an altogether better solution? Thanks.
NHibernate by default guarantees uniqueness of instances a session returns for a same entity. That is why your eager load returns the previously loaded lazy proxy if there was one in the same session for the entity (which by the way should then have been fully initialized).
You may have quite a hard time trying to dodge this, depending on how your application works.
You may Clear the session before your eager load for avoiding that, but this will cancel all pending changes and render any unloaded proxy unusable while any previously loaded entities would be detached from session.
You may instead Evict only your entity if you have a reference to it beforehand, but it does not look to be the case when reading your question.
I would rather adapt the mapping layer for it to support getting a proxy or the base entity class. This question is about that and has a bunch of interesting answers.
In your case, Diego Mijelshon answer's linked blog could suits you well for allowing your mapping layer to always get the concrete class.
It consists of adding a property to your entity class which will yield you the concrete instance whether you have already it or you have a proxy.
public virtual object Actual { get { return this; } }
As he warns, this is a hack. The concrete instance obtained by this method should not be used with the NHibernate session afterward.
In the process of refactoring an ASP.NET MVC 5 web project, I see an opportunity to move some select lists to another class where multiple controllers can access them. This would allow me to remove duplicate code.
In this instance, the select lists require a trip to the database. To hand-code the lists, which might change over time, would not be feasible (hence, the database query).
Although I have no compiler errors and the page appears to work as intended, I am not sure if I am creating other problems by taking this approach. Is the code approach shown below a "bad" way to achieve this outcome? Is there a better way to do this?
In summary, this is what I am doing:
The class and methods within are static
A private static readonly database context is defined at the top
The two functions shown query the database and produce the desired results.
Because this is a static class, there is no dispose method.
The Class:
public static class ElpLookupLists
{
private static readonly EllAssessmentContext Db = new EllAssessmentContext();
// code...
internal static IEnumerable<SelectListItem> StandardSelectList(string selectedDomain)
{
return Db.ElpStandardLists.Where(m => m.Domain == selectedDomain)
.Select(m => m.Standard).Distinct()
.Select(z => new SelectListItem { Text = z.ToString(), Value = z.ToString() }).OrderBy(z => z.Value)
.ToList();
}
internal static IEnumerable<SelectListItem> PerformanceIndicatorSelectList(string selectedDomain, int? selectedStandard,
string selectedSubConcept)
{
var query =
Db.ElpStandardLists.Where(m => m.Domain == selectedDomain).Where(m => m.Standard == selectedStandard);
if (!string.IsNullOrEmpty(selectedSubConcept)) query = query.Where(m => m.SubConcept == selectedSubConcept);
var list =
query.Select(m => m.PerformanceIndicator)
.Distinct().OrderBy(m => m)
.Select(z => new SelectListItem { Text = z.ToString(), Value = z.ToString() })
.OrderBy(z => z.Text).ToList();
return list;
}
}
In my opinion a better alternative would be to create a separate controller with methods to get this data, and you could OutputCache this method. You can then call this method in other controllers, and it won't make the database trip every time. The return value will be cached. You can control the cache settings of course.
The advantage of this technique over yours is that in your case, the database trip will always happen when the application starts because the method is static, irrespective of whether or not you are going to use it. Whereas by using a cached method, you make the database trip the first time you call the method.
I've came across this idea of updating a table inside of a LINQ query instead of making the query first, and updating each object returned by that query.
For instance, it is possible to change the value of any property associated with x inside of this query:
var Query = from x in EFContext.SomeTable
where x.id == 1
// SET X = Model or x.Name = "NewName"
select SaveChanges();
Could something like this be done at all?
From MSDN:
In a query that returns a sequence of values, the query variable itself
never holds the query results and only stores the query commands.
Execution of the query is deferred until the query variable is
iterated over in a foreach or for loop. This is known as deferred
execution; that is, query execution occurs some time after the query
is constructed. This means that you can execute a query as frequently
as you want to. This is useful when, for example, you have a database
that is being updated by other applications. In your application, you
can create a query to retrieve the latest information and repeatedly
execute the query, returning the updated information every time.
So, your query will be executed when you do the foreach to update your entities. As #recursive said, LINQ is useful when you need to do a query over a collection, not to update data specifically.
As an aditional info, you can also force immediate execution. This is useful when you want to cache the results of a query,for example, when you want to use some functionalities that Linq to Entities doesn't support. To force immediate execution of a query that does not produce a singleton value, you can call the ToList method, the ToDictionary method, or the ToArray method on a query or query variable.
I believe the best possible way to do so would be to write an extension method which can be done by creating a static class:
public static class Extensions
{
public static IEnumerable<T> Remove<T>(this DbSet<T> Input, Func<T, Boolean> Objects) where T : class
{
var I = Input.Where(Objects).ToList();
for (int i = 0; i < I.Count; i++)
{
Input.Remove(I[i]);
}
return Input;
}
public static IEnumerable<T> Update<T>(this DbSet<T> Input, Func<T, Boolean> Objects, Action<T> UpdateAction) where T : class
{
var I = Input.Where(Objects).ToList();
I.ForEach(UpdateAction);
return I;
}
}
Then you can do:
var Context = new EFContext();
Context.YourTable.Remove(x=> x.Id == 1);
Context.SaveChanges();
// OR
Context.Update((x=> x.Id == 1), (y)=> {y.Title = "something"});
Context.SaveChanges();
You could use method calls, and write a ForEach or ForEachWithContinue method that lets you modify each element, but EF wouldn't know what to do with it anyway, and you'd have to use ToList to pull the items out of EF before you could do anything to them.
Example of ForEach (functional purists won't like this of course):
public static void ForEach<T>(this IEnumerable<T> pEnumerable, Action<T> pAction) {
foreach (var item in pEnumerable)
pAction(item);
}
public static IEnumerable<T> ForEachWithContinue<T>(
this IEnumerable<T> pEnumerable,
Action<T> pAction
) {
foreach (var item in pEnumerable)
pAction(item);
return pEnumerable;
}
Then:
EFContext
.SomeTable
.Where(x => x .id == 1)
.ToList() // come out of EF
.ForEach(x => x.Name = "NewName");
EFContext.SaveChanges();
(Actually, List<T> even already has a ForEach method, too, so writing the IEnumerable extensions is not strictly necessary in this case.)
Basically, EF needs to pull the data into memory to know that you have changed anything, to know what your changes are, and to know what to save to back to the DB. I would also consider what it is you're trying to do, where you are overwriting data that neither the user nor the program has even looked at. How did you determine that this was data you wanted to overwrite in the first place?
Also, you can write direct SQL queries straight to the DB as well, using the ExecuteStoreCommand method, which would be the "normal" way of accomplishing this. Something like:
EFContext.ExecuteStoreCommand(
"UPDATE SomeTable SET Name = {0} WHERE ID = {1};",
"NewName",
1
);
I have a gridview, the datasource of which is the following function:
public static List<Train> GetTrainsByIDs(int [] ids) {
using (var context = new MyEntities())
{
return ids.Select(x => context.Trains.Single(y => y.TrainID ==x)).AsQueryable().Include(x=>x.Station).ToList();
}
}
The grid view has an ItemTemplate of <%# Eval("Station.Name") %>.
This causes the error The ObjectContext instance has been disposed and can no longer be used for operations that require a connection despite the fact that I used the include method.
When I change the function to
public static List<Train> GetTrainsByIDs(int [] ids) {
using (var context = new MyEntities())
{
return context.Trains.Where(x => ids.Contains(x.TrainID)).Include(x=>x.Station).ToList();
}
}
it works fine, but then they come out in the wrong order, and also if I have 2 ids the same I would like 2 identical trains in the list.
Is there anything I can do other than create a new viewmodel? Thank you for any help
As for the first query: that's deferred execution.You created an IEnumerable of Trains, noticed that it did not have the Include method, so cast it to IQueryable, added the Include and added the ToList() to prevent lazy loading.
But As per MSDN on DbExtensions.Include:
This extension method calls the Include(String) method of the IQueryable source object, if such a method exists. If the source IQueryable does not have a matching method, then this method does nothing.
(emphasis mine)
The result of the select is an IEnumerable converted to IQueryable, but now implemented by EnumerableQuery which does not implement Include. And nothing happens.
Now the data enters the grid which tries to display the station, which triggers lazy loading while the context is gone.
Apart from that, this design has another flaw: it fires a query for each id separately.
So the second query is much better. It is one query, including the Stations. But now the order is dictated by the order the database pleases to return. You could use Concat to solve this:
IQueryable<Train> qbase = context.Trains.Include(x=>x.Station);
IQueryable<Train> q = null;
foreach (var id in ids)
{
var id1 = id; // Prevent modified closure.
if (q == null)
q = qbase.Where(t => t.Id == id1);
else
q = q.Concat(qbase.Where (t => t.Id == id1));
}
The generated query is not very elegant (to say the least) but after all it is one query as opposed to many.
After reading #Gert Arnold's answer, and getting the idea of doing it in 2 stages, I managed very simply using the first query like this:
using (context = new MyEntities())
{
var trns = context.Trains.Include(x => x.Station);
return ids.Select(x => trns.Single(y => y.TrainID == x)).ToList();
}
Using LINQ to Entities with Entity Framework and C#:
I have a method that is called CanInactivateReason which checks that there are no active associated records before allowing the user to inactivate the reason. If the method returns false - then a 'DisabledDelete' image is displayed with a tooltip informing the user why they are unable to remove the entity in the grid instead of a 'Delete' image button. My problem is performance where the queries are returning all related objects instead of doing a top 1 for each navigation property.
.Take(1) extension method is not adding top(1) to my LINQ to Entities query - why??
Nor is .Count() > 0 or .Any() or .Take(1).ToArray().Any() or .FirstOrDefault() != null
Here is my method that returns bool so I'd rather the queries be top 1 - I tried each item below:
public bool CanInactivateReason(Reason reasonToInactivate)
{
bool canInactivate = true;
if (reasonToInactivate.ProductReasons.Select(pa => pa).Where(pa => pa.Inactive == false).Count() > 0)
{
canInactivate = false; // Still active products associated
}
if (reasonToInactivate.EnhancementReasons.Select(ea => ea).Where(ea => ea.Inactive == false).Any())
{
canInactivate = false; // Still active enhancements associated
}
if (reasonToInactivate.SuggestionReasons.Select(sa => sa).Where(sa => sa.Inactive == false).Take(1).ToArray().Any())
{
canInactivate = false; // still active suggestions associated
}
if ((reasonToInactivate.SessionProductReasons.Select(spr => spr).Where(spr => spr.Inactive == false).FirstOrDefault()) != null)
{
canInactivate = false; // Still active sessions associated
}
return canInactivate;
}
I am assuming this is due to accessing the related entities of my object, but what can I do to turn those queries into SQL generated top(1)?
Thanks in advance!
Working via navigation properties like this can cause issues. Depends on how you have LazyLoading configured, what your underlying model is (e.g. use of inheritance associations) and what the base query for reasonToInactivate is.
As a starting point I'd advise going back to basics with the query - just to prove the concept.
So something like (depending on your schema):
if(context.SuggestionReasonsEntitySet.Any(p => p.ReasonId == reasonToActivateId && p.IsActive))
{
canInactivate = false; // Still active enhancements associated
}
So side step the navigation property entirely. Navigation properties can be great, but they can also obscure what should be very simple queries. Personally I stick to them for select queries (i.e. to avoid having to do joins when creating a projection), but avoid them for filtering logic - just because I've been bitten a few times like this when using inheritance associations.
If that works then you can try working back towards your navigation property solution to find the point at which things go wrong.
If this isn't helpful - is the generated SQL from your queries above short enough to post (and be understood)? If so can you post it?