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

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);
}

Related

LINQ for SQL statement that returns single scalar value

I want to execute the following select:
SELECT 0 as Value
What is the correlating syntax in LINQ for SQL?
Edit
I want to use the correlating LINQ for SQL statement in a Concat() call like this
var c = (from a in mytable select a.Value).Concat(select 0).Sum();
As you can see, Concat(select 0) obviously doesn't compile. Any ideas?
Edit 2
David suggested to use a simple collection instead. I've tried
private decimal[] mZeroDecimals = new[] { 0.0m };
...
public void MyFunction()
{
var c = (from a in mytable select a.Value).Concat(mZeroDecimals).Sum();
...
but it throws an exception Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator.
You're not actually querying anything, so there is no LINQ involved. You're just creating an anonymous object with a single property called Value:
var obj = new { Value = 0 };
Edit: Based on your comment, it sounds like you want this object in a collection. That doesn't make it a LINQ query (since you're still not querying anything), but you can declare a collection just as easily as a single object. Something like this:
var coll = new[] { new { Value = 0 } };
Since this is a collection, it can be used with any of the enumerable extension methods that LINQ uses, which sounds like what you're trying to do.

How to aggregate IEnumerable<string> to string using Linq Aggregate function

I have Ienumerable<string> collection that I want to concatenate into a single string with delimitor ;
for instance {"One","Two","Three"} -> "One;Two;Three;"
is it possible to do using the following function?
List<string> list = new List<string>(){"One","Two","Three"};
list.Aggregate<String>((x,y) => x + String.Format("{0};",y));
I have tried also this code:
list.Aggregate<String>((x,y) => String.Format("{0};{1}",x,y));
both samples didn't work.
EDIT: I see that it is not possible to do what I wanted using Linq-2-sql or Aggregate function in Linq-2-sql.
http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/dac496c0-5b37-43ba-a499-bb8eff178706/
EDIT2: the workaround I used is to go over the items returned by the original linq query...and copies them to a new list and do the join as suggested in the answers below on a linq object and not linq-2-sql object.
You can just use String.Join for this. If you're using .NET4 then you can use the overload that takes an IEnumerable<string> directly:
string joined = string.Join(";", list);
If you're using an older version of the framework then you'll need to use the overload that takes a string[] array instead, converting your collection to an array first if necessary:
string joined = string.Join(";", list.ToArray());
EDIT...
Of course, if you really want to use Aggregate for some reason then there's nothing stopping you. If so, it's usually recommended to build your string using a StringBuilder rather than multiple string allocations:
string joined = list.Aggregate(new StringBuilder(),
(sb, s) => sb.Append(s).Append(';'),
sb => (sb.Length > 0) ? sb.ToString(0, sb.Length - 1)
: "");
You can do it using below code
list.Aggregate((i, j) => i + ";" + j);
You'll need to provide an initializer, otherwise the first element will not have a ; added to it:
list.Aggregate<String>("", (x,y) => x + String.Format("{0};",y));

Entity Framework 4.0 Code-First Dynamic Query

I would like to query a table based on a list of KeyValuePair. With a Model-First approach, I could do the following:
var context = new DataContext();
var whereClause = new StringBuilder();
var objectParameters = new List<ObjectParameter>();
foreach(KeyValuePair<string, object> pair in queryParameters)
{
if (whereClause.Length > 0)
whereClause.Append(" AND ");
whereClause.Append(string.Format("it.[{0}] = #{0}", pair.Key));
parameters.Add(new ObjectParameter(pair.Key, pair.Value));
}
var result = context.Nodes.Where(whereClause.ToString(), parameters.ToArray());
Now I'm using a Code-First approach and this Where method is not available anymore. Fortunately, I saw an article somewhere (I can't remember anymore) which suggested that I could convert the DbContext to a IObjectContextAdapter then call CreateQuery like this:
var result = ((IObjectContextAdapter)context)
.ObjectContext.CreateQuery<Node>(whereClause.ToString(), parameters.ToArray());
Unfortunately, this throws an error:
'{ColumnName}' could not be resolved in the current scope or context. Make sure that all referenced variables are in scope, that required schemas are loaded, and that namespaces are referenced correctly.
Where {ColumnName} is the column specified in the whereClause.
Any ideas how I can dynamically query a DbSet given a list of key/value pairs? All help will be greatly appreciated.
I think your very first problem is that in the first example you are using Where on the entity set but in the second example you are using CreateQuery so you must pass full ESQL query and not only where clause! Try something like:
...
.CreateQuery<Node>("SELECT VALUE it FROM ContextName.Nodes AS it WHERE " + yourWhere)
The most problematic is full entity set name in FROM part. I think it is defined as name of the context class and name of the DbSet exposed on the context. Another way to do it is creating ObjectSet:
...
.ObjectContext.CreateObjectSet<Node>().Where(yourWhere)

LINQ: select an object, but change some properties without creating a new object

I'm trying to select an object using values of another object in LINQ SQL,
I currently have this,
var result1 = (from s in pdc.ScanLogs
from ec in pdc.ExhibitsContacts
where s.ExhibitID == ec.ExhibitID
select ec.Contact);
I want to assign a value of ec.Contact.Note = ec.Comment;
Is there to a way to do this in LINQ SQL without writing multiple queries?
I read this blog article: http://blog.robvolk.com/2009/05/linq-select-object-but-change-some.html but it doesn't seem to work with LINQ SQL.
Basically you can't do this. LINQ is meant to be a query language, and what you want to do is mutate existing entities with your query. This means your query would have side effects and this is not something that is supported by LINQ to SQL.
While this won't work in a single query while returning LINQ to SQL entities, what will work is when you return simple DTO structues. For instance:
var result1 =
from s in pdc.ScanLogs
from ec in s.ExhibitsContacts
select new ContactDto
{
Id = ec.Contact.Id,
Note = ec.Comment,
SomeOtherFields = ec.Contact.SomeOtherFields
};
As a side note: also look at how I removed the where s.ExhibitID == ec.ExhibitID join from the query, by just using the ExhibitsContacts property of the ScanLog entity (which will be generated by LINQ to SQL for you when your database schema has the proper foreign keys defined).
Update:
When you need to return those DTO from several methods, you might consider centralizing the transformation from a collection of entities to a collection of DTO objects. What I tend to do is place this method on the DTO (which makes it easy to find). The code might look like this:
public class ContactDto
{
// Many public properties here
public static IQueryable<ContactDto> ToDto(
IQueryable<Contact> contacts)
{
return
from contact in contacts
select new ContactDto
{
Id = contact.Id,
Note = contact.ExhibitsContact.Comment,
ManyOtherFields = contact.ManyOtherFields
};
}
}
The trick with this static transformation method is that it takes an IQueryable and returns an IQueryable. This allows to to simply specify the transformation and let LINQ to SQL (or any other LINQ enabled O/RM) to efficiently execute that LINQ expression later on. The original code would now look like this:
IQueryable<Contact> contacts =
from s in pdc.ScanLogs
from ec in s.ExhibitsContacts
select ec.Contact;
IQuerable<ContactDto> result1 = ContactDto.ToDto(contacts);
the problem is that LINQ to SQL does not know how to interpret your extension method. The only way, other than using stored procedures from LINQ to SQL (which kind of defeats the ponit), is to get the object, update and then commit changes.

exception in 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