exception in Linq to sql - linq-to-sql

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

Related

Entity Framework 5 - T4 generated context class causing 'duplicate parameter name'

I'm using EF5.0 in an ASP.NET MVC app. My Entity Model is named 'DataModel'. Included in the model is a table-valued function that exists in my MSSQL database, named MatchingEntries. It returns a table of integer ids.
I've looked at the DataModel.Context.cs file, that gets generated via the .tt (T4) template file. It has the following code in it:
[EdmFunction("DataEntities", "MatchingEntries")]
public virtual IQueryable<Nullable<int>> MatchingEntries(string term)
{
var termParameter = term != null ?
new ObjectParameter("Term", term) :
new ObjectParameter("Term", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<Nullable<int>>("[DataEntities].[MatchingEntries](#Term)", termParameter);
}
The error I am getting results from using this method twice within the one query, such as:
IQueryable<int> one = db.MatchingEntries("\"one*\"");
IQueryable<int> two = db.MatchingEntries("\"two*\"");
List<int> both = one.Intersect(two).ToList();
The error is:
A parameter named 'Term' already exists in the parameter collection. Parameter names must be unique in the parameter collection.
Parameter name: parameter
Is this a known limitation of the classes generated from an EDMX for table-valued functions? With LINQ2SQL I am able to execute this a a single query to the database (that does a JOIN between the 2 outputs from MatchingEntries) and it replaces the parameter name #Term with #p0 and #p1 for the two different instances of the call. I'd like to make Entity Framework do the same.
So, my question is, how can I get EF to work in the same manner and avoid the 'Duplicate parameter' error?
My fallback is to evaluate each call to db.MatchingEntries separately, by putting ToList() after them. My other idea has been to replace the ObjectParameter name in the T4 generated Context.cs class with something randomly generated each time. These feel like hacks that I should be able to avoid.
This answer is Linq to Entities specific. This doesn't have to be done in Linq to SQL (Linqpad).
Thanks to this question I got a pointer to a viable solution:
extend the autogenerated DBContext class (partial class)
add a method with two parameters in the partial class
at calling, pass an index as second parameter
Detailed Answer:
DataEntitys.my.cs:
[EdmFunction("DataEntities", "MatchingEntries")]
public virtual IQueryable<Nullable<int>> MatchingEntries(string term, int index)
{
string param_name = String.Format("k_{0}", index);
var termParameter = term != null ?
new ObjectParameter(param_name, term) :
new ObjectParameter(param_name, typeof(string));
return ((IObjectContextAdapter)this).
ObjectContext.CreateQuery<Nullable<int>>(
String.Format("[DataEntities].[MatchingEntries](#{0})", param_name),
termParameter);
}
Call the function:
foreach (string teil in such)
{
index++;
if (teil.Trim() != "")
res = res.Join(db.MatchingEntries("\"" + teil + "*\"", index), l => l.ID, s => s.KEY, (l, s) => l);
}

Why does this object need a DataContext?

This is weird.
regiao C = new regiao();
C.Nome = textBoxNome.Text;
C.Descricao = textBoxDescicao.Text;
C.cidades.AddRange(ListaIN);
mgrRegiao mgr = new mgrRegiao();
mgr.UpdateRegiao(C);
C is a Linq object (code generated by VS2008 in the dbml file) the operation is out of any context. ListaIN in is of type List. That is also the type of C.cidades. The call to mgr.UpdateRegiao creates a context, Copies the contents to objects inside the context and updates the objects. Some of the itens in ListN may have come from a Context (from a query result)
When
C.cidades.AddRange(ListaIN)
Executes I get an exception
Cannot access a disposed object.
Object name: 'DataContext accessed after Dispose.'.
at the line
regiao previousValue = this._regiao.Entity;
in the generated code for the entity regiao set opeation.
[Association(Name="regiao_cidade", Storage="_regiao", ThisKey="IDRegiao", IsForeignKey=true)]
public regiao regiao
{
get
{
return this._regiao.Entity;
}
set
{
regiao previousValue = this._regiao.Entity;
if (((previousValue != value)
|| (this._regiao.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._regiao.Entity = null;
previousValue.cidades.Remove(this)
...
Whats wrong ? The object C does not have and never had a context. Why it needs a context and how do I solve this ? Is it because some itens in ListaIN came from the database ? If so, is there a way to detach them ?
OK, so that just the way Linq is, i guess.

Using Linq to update a List<T> of objects to DB

I have a form that returns me a List of FlatSessie objects
in my edit view I edit a few FlatSessie's and get them returned to my Post method in that List object.
In my DB I have Sessies, which I map using Automapper to FlatSessie's and back
now I can not get linq to make the update to the DB for me.
the code:
[HttpPost]
public ActionResult Sessies(int id, int? modID, int? projID, string schooljaarparam, List<FlatSessie> sl) {
if (ModelState.IsValid) {
foreach (FlatSessie s in sl) { //i run over all FlatSessies which i get
Models.Sessies ses = Mapper.Map<FlatSessie, Sessies>(s); // i map them to the Sessies object
List<Sessies> origReeks = _db.Sessies.Where(p => p.Ses_ID == ses.Ses_ID).ToList(); //i get the original Session by ID. if there is a Session with that ID, if not (the ID will be 0) i do an Insert. if there is i want to do an Update.
if (origReeks.Count > 0) {
//it's an update
UpdateModel(origReeks.First(); //doesnt work
//_db.Sessies.Attach(ses, origReeks.First()); //doesnt work, gives me an error on used ID...
_db.SubmitChanges();
} else {
//no sessies yet, add them, this works.
_db.Sessies.InsertOnSubmit(ses);
_db.SubmitChanges();
}
}
TempData["okmsg"] = "De sessies zijn opgeslagen";
return RedirectToAction("Index");
}
//if not valid, i return the viewdata which i need.
Module m = _db.Modules.First(md => md.Mod_ID == modID.Value);
int antses = m.Mod_AantalSessies.Value;
List<List<SelectListItem>> lpllst = new List<List<SelectListItem>>(antses);
for (int i = 0; i < antses; i++) {
lpllst.Add(MvcApplication.lesplaatsList(schooljaarparam, -1));
}
ViewData["lesplist"] = lpllst;
ViewData["lglist"] = MvcApplication.lesgeverList();
return View(sl);
}
It might work to provide a prefix to UpdateModel (FlatSessie[n], where n is such that it matches the actual input name of the model element in question) so that it knows which properties to map onto the object, but because you are getting a list of these it might not. Have you tried updating the retrieved model using the data from the matching FlatSessie object directly?
Also, once you get this to work, you might want to do a single SubmitChanges for all inserts/updates (outside the loop) so that you get the entire submit wrapped in a single transaction. This will make it easier if there are errors to correct them and resubmit -- since you won't have some changes already committed causing further potential errors.

Need help building LINQ to SQL Expression

I need to translate the following Code to an Expression and I will explain why:
results = results.Where(answer => answer.Question.Wording.Contains(term));
results is IQueryable<ISurveyAnswer>
Question is ISurveyQuestion
Wording is String
The problem is, Question is not always the name of the LINQ to SQL property.
This will give me the PropertyInfo for the actual ISurveyQuestion property
private static PropertyInfo FindNaturalProperty<TMemberType>(Type search)
{
IDictionary<string,PropertyInfo> properties = new Dictionary<string,PropertyInfo>();
search.GetProperties().Each(prop =>
{
if (null != prop.PropertyType.GetInterface(typeof(TMemberType).Name))
properties.Add(prop.Name, prop);
});
if (properties.Count < 1) throw new ArgumentException(String.Format("{0} has no properties of type {1}", search.Name, typeof(TMemberType).Name));
if (properties.Count == 1) return properties.Values.First();
search.GetInterfaces().Each(inter =>
{
inter.GetProperties().Each(prop =>
{
if (null != prop.PropertyType.GetInterface(typeof(TMemberType).Name))
properties.Remove(prop.Name);
});
});
if (properties.Count < 1) throw new ArgumentException(String.Format("{0} has no properties of type {1} that are not members of an interface", search.Name, typeof(TMemberType).Name));
if (properties.Count > 1) throw new AmbiguousMatchException(String.Format("{0} has more than one property that are of type {1} and are not members of an interface", search.Name, typeof(TMemberType).Name));
return properties.Values.First();
}
Once I have the PropertyInfo how do I translate that to an Expression Tree?
EDIT:
What I basically need is:
results = results.Where(answer => answer.GetQuestionProperty().GetValue(answer).Wording.Contains(term));
But that doesn't work so I need to build the Expression Tree myself, for linq-to-sql.
Reading the question I think what you're after is Dynamic Linq - which is a helper library to let you build Linq queries dynamically (!) using strings as opposed to at design time. That means that if you can get your property name you should be able create your query on the fly.
ScottGu has an article here
What your trying to do is create a dynamic query and you want the action tables / properties your query against to be dynamic as well. I am not sure if this is easily possible based on how you want to use it.
Check out ScottGu's blog post:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
and
Check out Rick Strahl's blog post:
http://www.west-wind.com/Weblog/posts/143814.aspx
http://www.linqpad.net/
linqpad will convert it for you.

How do I use Count and Group in a select query in Linq?

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.