I use this query in controller for creating a list from join four tables:
var ViewModel = (from APP in db1.application
where APP.APP_STATUS == "TO_DO"
join APPDEL in db1.app_delegation on APP.APP_UID equals APPDEL.APP_UID
join Con in db1.content on APPDEL.TAS_UID equals Con.CON_ID
join BPMNPRC in db1.bpmn_process on APPDEL.PRO_UID equals BPMNPRC.PRJ_UID
join RUSRE in db1.rbac_users on APP.APP_INIT_USER equals RUSRE.USR_UID
where APPDEL.TAS_UID == Con.CON_ID
&& Con.CON_CATEGORY == ("TAS_TITLE")
&& APP.APP_UID == APPDEL.APP_UID
&& APPDEL.DEL_THREAD_STATUS == "OPEN"
&& APPDEL.USR_UID == bpmUseid.BPMSID
select new ApplicationContentViewModel
{
creator = RUSRE,
app_delegation = APPDEL,
application = APP,
content = Con,
task = BPMNPRC,
}).AsEnumerable();
return View(ViewModel);
But I need to select a special record in APPDEL (app_delegation), then retrieve name of this record.
I use this command in view for selecting item by field of DEL_INDEX:
#foreach (var item in Model)
{
<th>
#(item.app_delegation.DEL_INDEX == Model.Max(x => x.app_delegation.DEL_INDEX) - 1 ? item.creator.USR_LASTNAME : "");
</th>
}
In fact, I want to select the max value of DEL_INDEX - 1, and retrieve its last name.
Unfortunately, I get this error:
System.Data.Entity.Core.EntityCommandExecutionException:: 'An error occurred while executing the command definition. See the inner exception for details.'
Inner Exception:
MySqlException: There is already an open DataReader associated with this Connection which must be closed first
How can I handle this error?
I think you should use ToList() method instead of AsEnumerable() before executing expression Model.Max(x => x.app_delegation.DEL_INDEX) as shown below:
var ViewModel = (from APP in db1.application
where APP.APP_STATUS == "TO_DO"
join APPDEL in db1.app_delegation on APP.APP_UID equals APPDEL.APP_UID
join Con in db1.content on APPDEL.TAS_UID equals Con.CON_ID
join BPMNPRC in db1.bpmn_process on APPDEL.PRO_UID equals BPMNPRC.PRJ_UID
join RUSRE in db1.rbac_users on APP.APP_INIT_USER equals RUSRE.USR_UID
where APPDEL.TAS_UID == Con.CON_ID
&& Con.CON_CATEGORY == ("TAS_TITLE")
&& APP.APP_UID == APPDEL.APP_UID
&& APPDEL.DEL_THREAD_STATUS == "OPEN"
&& APPDEL.USR_UID == bpmUseid.BPMSID
select new ApplicationContentViewModel
{
creator = RUSRE,
app_delegation = APPDEL,
application = APP,
content = Con,
task = BPMNPRC,
}).ToList(); // here is the change
Explanation
By calling AsEnumerable() the data reader is kept open and when executing Model.Max() as LINQ to Objects throws exception due to MySQL Connector you're using doesn't have support for MARS (Multiple Active Result Sets) yet. Using ToList() methods ensures the data reader operation is completed when lazy loading checks all navigation properties.
Side note:
Assumed you're using EF, try eager loading with Include() to add related objects in single statement when dealing with multiple queries.
Similar issue:
Mysql Linq There is already an open DataReader associated with this Connection which must be closed first
Related
I have a struts2 app with spring transactions and JPA2 over hibernate. The problem is that I have some rows in the database that are changed by an external source (some mysql triggers) and in my front app I have an ajax script that checks this values every 2 seconds. I always need to get the latest value, and not a cached one, and for this I found 2 solutions :
String sql = "FROM MyEntity WHERE xId=:id AND connect!=0 AND complete=0 AND (error=NULL OR error=0)";
Query q = this.em.createQuery(sql).setHint("org.hibernate.cacheable", false).setParameter("agId", agentId);
rs = q.getResultList();
if(rs.size() == 1){
intermedObj = (Intermed) rs.get(0);
}
and the other:
String sql = "FROM MyEntity WHERE xxId=:id AND connect!=0 AND complete=0 AND (error=NULL OR error=0)";
Query q = this.em.createQuery(sql).setParameter("agId", agentId);
rs = q.getResultList();
if(rs.size() == 1){
intermedObj = (Intermed) rs.get(0);
//get latest object from DB
em.refresh(intermedObj);
}
em is a instance of EntityManager which is managed by spring.
So, the question is: which is the best approach from these 2? Or maybe there is a better one ?
So you are right, I used hql there, I still have to learn a lot about hibernate an jpa, and java in general. So I guess that the correct way to write that cod in JPQL would be:
String sql = "SELECT m FROM MyEntity m WHERE m.xxId=:id AND m.connect!=0 AND m.complete=0 AND (m.error!=1)";
Query q = this.em.createQuery(sql).setParameter("agId", agentId);
rs = q.getResultList();
if(rs.size() == 1){
intermedObj = (Intermed) rs.get(0);
//get latest object from DB
em.refresh(intermedObj);
}
So the question would be, is this the proper way to make sure that I got the latest row from DB and not a cached record?
As regarding leve2 cache question I do not know if this is activated. How do I check that?
I have the following problem: In our database we record helpdesk tickets and we book hours under tickets. Between those is a visit report. So it is: ticket => visitreport => hours.
Hours have a certain 'kind' which is not determined by a type indicator in the hour record, but compiled by checking various properties of an hour. For example, an hour which has a customer but is not a service hour is always an invoice hour.
Last thing I want is that the definitions of those 'kinds' roam everywhere in the code. They must be at one place. Second, I want to be able to calculate totals of hours from various collections of hours. For example, a flattened collection of tickets with a certain date and a certain customer. Or all registrations which are marked as 'solution'.
I have decided to use a 'layered' database access approach. The same functions may provide data for screen representation but also for a report in .pdf . So the first step gathers all relevant data. That can be used for .pdf creation, but also for screen representation. In that case, it must be paged and ordered in a second step. That way I don't need separate queries which basically use the same data.
The amount of data may be large, like the creation of year totals. So the data from the first step should be queryable, not enumerable. To ensure I stay queryable even when I add the summation of hours in the results, I made the following function:
public static decimal TreeHours(this IEnumerable<Uren> h, FactHourType ht)
{
IQueryable<Uren> hours = h.AsQueryable();
ParameterExpression pe = Expression.Parameter(typeof(Uren), "Uren");
Expression left = Expression.Property(pe, typeof(Uren).GetProperty("IsOsab"));
Expression right = Expression.Constant(true, typeof(Boolean));
Expression isOsab = Expression.Equal(Expression.Convert(left, typeof(Boolean)), Expression.Convert(right, typeof(Boolean)));
left = Expression.Property(pe, typeof(Uren).GetProperty("IsKlant"));
right = Expression.Constant(true, typeof(Boolean));
Expression isCustomer = Expression.Equal(Expression.Convert(left, typeof(Boolean)), Expression.Convert(right, typeof(Boolean)));
Expression notOsab;
Expression notCustomer;
Expression final;
switch (ht)
{
case FactHourType.Invoice:
notOsab = Expression.Not(isOsab);
final = Expression.And(notOsab, isCustomer);
break;
case FactHourType.NotInvoice:
notOsab = Expression.Not(isOsab);
notCustomer = Expression.Not(isCustomer);
final = Expression.And(notOsab, notCustomer);
break;
case FactHourType.OSAB:
final = Expression.And(isOsab, isCustomer);
break;
case FactHourType.OsabInvoice:
final = Expression.Equal(isCustomer, Expression.Constant(true, typeof(Boolean)));
break;
case FactHourType.Total:
final = Expression.Constant(true, typeof(Boolean));
break;
default:
throw new Exception("");
}
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { hours.ElementType },
hours.Expression,
Expression.Lambda<Func<Uren, bool>>(final, new ParameterExpression[] { pe })
);
IQueryable<Uren> result = hours.Provider.CreateQuery<Uren>(whereCallExpression);
return result.Sum(u => u.Uren1);
}
The idea behind this function is that it should remain queryable so that I don't switch a shipload of data to enumerable.
I managed to stay queryable until the end. In step 1 I gather the raw data. In step 2 I order the data and subsequently I page it. In step 3 the data is converted to JSon and sent to the client. It totals hours by ticket.
The problem is: I get one query for the hours for each ticket. That's hundreds of queries! That's too much...
I tried the following approach:
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Ticket>(t => t.Bezoekrapport);
options.LoadWith<Bezoekrapport>(b => b.Urens);
dc.LoadOptions = options;
Bezoekrapport is simply Dutch for 'visitreport'. When I look at the query which retrieves the tickets, I see it joins the Bezoekrapport/visitreport but not the hours which are attached to it.
A second approach I have used is manually joining the hours in LINQ, but that does not work as well.
I must do something wrong. What is the best approach here?
The following code snippets are how I retrieve the data. Upon calling toList() on strHours in the last step, I get a hailstorm of queries. I've been trying for two days to work around it but it just doesn't work... Something must be wrong in my approach or in the function TreeHours.
Step 1:
IQueryable<RelationHoursTicketItem> HoursByTicket =
from Ticket t in allTickets
let RemarkSolved = t.TicketOpmerkings.SingleOrDefault(tr => tr.IsOplossing)
let hours = t.Bezoekrapport.Urens.
Where(h =>
(dateFrom == null || h.Datum >= dateFrom)
&& (dateTo == null || h.Datum <= dateTo)
&& h.Uren1 > 0)
select new RelationHoursTicketItem
{
Date = t.DatumCreatie,
DateSolved = RemarkSolved == null ? (DateTime?)null : RemarkSolved.Datum,
Ticket = t,
Relatie = t.Relatie,
HoursOsab = hours.TreeHours(FactHourType.OSAB),
HoursInvoice = hours.TreeHours(FactHourType.Invoice),
HoursNonInvoice = hours.TreeHours(FactHourType.NotInvoice),
HoursOsabInvoice = hours.TreeHours(FactHourType.OsabInvoice),
TicketNr = t.Id,
TicketName = t.Titel,
TicketCategorie = t.TicketCategorie,
TicketPriority = t.TicketPrioriteit,
TicketRemark = RemarkSolved
};
Step 2
sort = sort ?? "TicketNr";
IQueryable<RelationHoursTicketItem> hoursByTicket = GetRelationHours(relation, dateFrom, dateTo, withBranches);
IOrderedQueryable<RelationHoursTicketItem> orderedResults;
if (dir == "ASC")
{
orderedResults = hoursByTicket.OrderBy(sort);
}
else
{
orderedResults = hoursByTicket.OrderByDescending(sort);
}
IEnumerable<RelationHoursTicketItem> pagedResults = orderedResults.Skip(start ?? 0).Take(limit ?? 25);
records = hoursByTicket.Count();
return pagedResults;
Step 3:
IEnumerable<RelationHoursTicketItem> hours = _hourReportService.GetRelationReportHours(relation, dateFrom, dateTo, metFilialen, start, limit, dir, sort, out records);
var strHours = hours.Select(h => new
{
h.TicketNr,
h.TicketName,
RelationName = h.Relatie.Naam,
h.Date,
TicketPriority = h.TicketPriority.Naam,
h.DateSolved,
TicketCategorie = h.TicketCategorie == null ? "" : h.TicketCategorie.Naam,
TicketRemark = h.TicketRemark == null ? "" : h.TicketRemark.Opmerking,
h.HoursOsab,
h.HoursInvoice,
h.HoursNonInvoice,
h.HoursOsabInvoice
});
I don't think your TreeHours extension method can be converted to SQL by LINQ in one go. So are evaluated on execution of each constructor of the row, causing a 4 calls to the database in this case per row.
I would simplfy your LINQ query to return you the raw data from SQL, using a simple JOIN to get all tickets and there hours. I would then group and filter the Hours by type in memory. Otherwise, if you really need to perform your operations in SQL then look at the CompiledQuery.Compile method. This should be able to handle not making a query per row. I'm not sure you'd get the switch in there but you may be able to convert it using the ?: operator.
In my mvc web application, I am getting this error:
Anonymously Hosted DynamicMethods Assembly
Stack Trace : at Read_<>f__AnonymousType14(ObjectMaterializer1 ) at System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader`2.MoveNext() at project.com.Concrete.DetailsRepository.GetDetails(String type) in path
Message : The null value cannot be assigned to a member with type System.Int32 which is a non-nullable value type.
When I run my site from local server it is working fine.
But when it runs at remote server it is giving above error
Here is my code:
var res=
(from r in DetailsTable
where r.Activated == true
group r by new { r.ActivationDate, r.ProductID, r.SubProductID } into t
select new { icount = t.Count(),
sActivationDate = t.Key.ActivationDate.ToShortDateString(),
iProductID = t.Key.ProductID,
iSubProductid = t.Key.SubProductID })
.OrderBy(r => r.icount);
Thanks
AS
The issue you're having is that your query is empty on the remote server where data exists on the local server.
I'm not exactly sure at which part in the query the exception is happening, so I'd suggest splitting your query in half.
var res=
from r in DetailsTable
where r.Activated == true;
if(res.Count() == 0)
return; // or handle gracefully as appropriate
var groups =
from r in res
group r by new { r.ActivationDate, r.ProductID, r.SubProductID } into t
select new { icount = t.Count(),
sActivationDate = t.Key.ActivationDate.ToShortDateString(),
iProductID = t.Key.ProductID,
iSubProductid = t.Key.SubProductID })
.OrderBy(r => r.icount);
I'm sure there is a more graceful way of doing this within a single query statement, but without more details I'm not sure exactly how to proceed.
my query is :
var ReadAndUnreadMessages =
(from m in MDB.Messages
orderby m.Date descending
where m.ID_Receive == (Guid)USER.ProviderUserKey && m.Delete_Admin == false
select new AllMessages()
{
id = (LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).ID_Message,
parent = (Guid)(LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).ID_Message_Parent,
sender = (LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).ID_Sender,
receiver = (Guid)USER.ProviderUserKey,
subject = (LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).Subject.Subject1.ToString() == "Other" ?
(LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).Other_Subject
:
(LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).Subject.Subject1.ToString(),
body = (LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).Body.Length > 26 ?
(LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).Body.Substring(0, 25) + "..."
:
(LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).Body,
date = (LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).Date.ToShortDateString(),
read =(LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).IsRead,
finished = (LoadMessageChildren(m.ID_Message)[LoadMessageChildren(m.ID_Message).Count - 1] as Message).IsFinished,
count = MessageClass.LoadAll(m.ID_Message).Count
}).ToList();
and exception is :
The argument 'value' was the wrong type. Expected 'Message'. Actual 'System.Object'.
what does meaning it?
LoadMessageChildren :
public static ArrayList LoadMessageChildren(Guid Parent)
{
ArrayList arr = new ArrayList();
Guid id = Parent;
while (id != Guid.Empty)
{
arr.Add(LoadMessage(id));
try
{
id = (Guid)MDB.Messages.Single(a => a.ID_Message_Parent == id).ID_Message;
}
catch
{
id = Guid.Empty;
}
}
return arr;
}
LoadMessage :
public static Message LoadMessage(Guid id)
{
var mess = from m in MDB.Messages
where m.ID_Message == id
select m;
return mess.Single();
}
The code is unreadable, and as a bad case of code repetition (and multiple executions of LoadMessageChildren).
For starters, consider the following:
from m in MDB.Messages
orderby m.Date descending
where m.ID_Receive == (Guid)USER.ProviderUserKey && m.Delete_Admin == false
let children = LoadMessageChildren(m.ID_Message)
let lastChildMessage = children.Last()
select new AllMessages()
{
id = lastChildMessage.ID_Message,
...
}
This may solve your problem, as it is might be caused by using the [] indexer.
Aside from that, it is not clear the posted code is causing the exception.
The only thing I see you using LoadChildMessages() for in the end is to get the child message count... Unless I am wrong I would think you could write it as a join. You doing a lot of queries with in queries that don't seem necessary and are probably causing multiple hits to the database. My question to that would be why isn't there a relationship in your dmbl/sql database so that LinqToSql knows to create a property as a List<Message> ChildMessages
But here is my take:
var query = from message in MDB.Messges
join childmessage in MDB.Messages.Where(child => child.ID_Message_Parent == message.ID_Message) into childMessages
from childMessage in childMessages.DefaultIfEmpty() // This creates a
// left outer join so you get parent messages that don't have any children
where message.ID_Receive == (Guid)USER.ProviderUserKey && message.Delete_Admin == false
select new AllMessages()
{
id = message.ID_Message,
parent = message.ID_Message_Parent,
sender = message.ID_Sender,
receiver = (Guid)USER.ProviderUserKey,
subject = message.Subject.Subject1.ToString() == "Other" ?
message.Other_Subject
:
message.Subject.Subject1.ToString(),
body = message.Body.Length > 26 ?
message.Body.Substring(0, 25) + "..."
:
message.Body,
date = message.Date.ToShortDateString(),
read =message.IsRead,
finished = message.IsFinished,
count = childMessage.Count() // This might have to be this
//count = childMessage == null ? 0 : childMessage.Count()
};
var ReadAndUnreadMessages = query.ToList();
But it's hard to say because I can't run the code... Please respond and let me know if this works.
Note: May I suggest using a class that links to your DataContext.Log property that writes the generated TSQL code to the debugger window. Here is an article on writing your own. It has really help me know when I am making unnecessary calls to the database.
The error is most likely caused by the use of the ArrayList.
The problem is that LINQ was designed to work with generic collections that implement the System.Collections.Generic.IEnumerable<T> interface. The ArrayList is a nongeneric collection that internally stores everything as an Object. So when you retrieve something from the ArrayList you need to cast it to a Message.
Looking at your error message it looks like somewhere a Message object is expected, but the instance in your ArrayList (an Object) is not casted to a Message object when that reference occurs. Also, the ArrayList does not implement the IEnumerable<T> interface which might get you into trouble in certain situations also.
How to fix it?
I suggest changing the implementation of your LoadMessageChildren to use a generic list (List<Message>):
public static List<Message> LoadMessageChildren(Guid Parent)
{
List<Message> arr = new List<Message>();
Guid id = Parent;
while (id != Guid.Empty)
{
arr.Add(LoadMessage(id));
try
{
id = (Guid)MDB.Messages.Single(a => a.ID_Message_Parent == id).ID_Message;
}
catch
{
id = Guid.Empty;
}
}
return arr;
}
You will have to make also change the code that interacts with the generic list in terms of retrieving/referencing items. But that is just syntax. Since equivalent methods for dealist with lists and items exist.
There are also advantages in terms of performance and compile-time validation for switching from ArrayList to List<T>. The ArrayList is basically an inheritance from version 1.0 of the .Net Framework when there was no support for generics and it just get kept in the framework probably for compatibility reasons.
There are greater benefits for using generics.
UPDATED ANSWER:
The "Method 'System.Collections.Generic.List'1[Message] LoadMessageChildren(System.Guid)' has no supported translation to SQL" exception that you are getting is caused by the fact that your LoadMessageChildren method is not mapping to a stored procedure or a user defined function in your database.
You cannot have any regular C# method call inside your LINQ to SQL queries. The LINQ to SQL object model interprets a method found inside your query as either a stored procedure or a user defined function. So the engine is basically looking for a method called LoadMessageChildren that maps to a stored procedure or a user defined function in your database. Because there are no mappings, it tells you that no supported translation to SQL was found. The LINQ to SQL object model link shows you how to use method attributes to map a method that executes a stored procedure.
You have a few choices now:
create stored procedures of your regular C# method calls
rewrite your LINQ query to use joins to select child messages
I had the following Linq code:
var allRequests = model.GetAllRequests();
var unsatisifedRequests = (from request in allRequests
where request.Satisfied == false
select request)
.OrderBy(r => r.RequestedOn)
.GroupBy(r => r.RequestedCountryId);
After which I then did a foreach over unsatifiedRequests building a new TOARequestListSummary object for each. This meant if I "returned" 4 items from the query, it would make 4 calls to the DB, once per loop of the foreach to grab the individual rows.
This seems to be the wrong way to use Linq, so I tries to convert this query to one which used projections to return the TOARequestListSummary objects directly and I came up with:
var summaries = (from request in allRequests
where request.Satisfied == false
group request by request.RequestedCountryId into requestGroups
select new TOARequestListSummary
{
CountryName = requestGroups.First().RequestedCountry.Description,
RequestCount = requestGroups.Count(),
FirstRequested = requestGroups.First().RequestedOn
});
But when I run this, I get the following exception:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
I have got as far as knowing that the Linq equivalent to EXISTS is Contains, but I have no idea how to indroduce this into the query.
This should work for you:
var summaries = (from request in allRequests
where request.Satisfied == false
group request by request.RequestedCountry into g
select new TOARequestListSummary
{
CountryName = g.Key.Description,
RequestCount = g.Count(),
FirstRequested = g.Min(i => i.RequestedOn)
});
In your original version of this query (the second one you posted), your group's key was the RequestedCountryId. Though this will technically be grouping on that, you actually want to use the associated object. This way you'll have easy access to the needed properties and won't need to worry about grabbing the first item.
Sorry, this is an answer, rather than an additional comment to Ryan's answer, but it is too long to fit...
This gets very strange. In LinqPad the following works a treat:
from request in TOARequests
where request.Satisfied == false
&& request.Active == true
orderby request.RequestedOn
group request by request.RequestedCountry into g
select new
{
CountryName = g.Key.Description,
RequestCount = g.Count(),
FirstRequested = g.First().RequestedOn
}
But the following throws the same translation exception in C#
var summaries = (from request in context.Repository<TOARequest>()
where request.Satisfied == false
&& request.Active == true
orderby request.RequestedOn
group request by request.RequestedCountry into g
select new
{
CountryName = g.Key.Description,
RequestCount = g.Count(),
FirstRequested = g.First().RequestedOn
}).ToList();
The only difference I can see if the ToList(), but even without that when I try to enumerate the list, it throws the exception.