Entity Framework 4.0 Code-First Dynamic Query - entity-framework-4.1

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)

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

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.

How to bind gridview through linq

I am using Linq-to-SQL.
Currently I am binding gridview through linq which query written in business logic call. I have extract record through query in business logic class and I want to bind that particular data to gridview and return data.
How to return data which type is array?
The code is here:
CMSBusiness.DataClasses1DataContext db = new DataClasses1DataContext();
var cate =
from p in db.categoryTables
select new
{
categoryId=p.categoryId,
categoryName=p.categoryName,
categoryDesc=p.categoryDesc
};
How to return value and bind gridview?
Try gridView.DataSource = cate;, this should work.
We also recommend you to take a look at this article.

How to alter this LINQ to SQL so it doesn't bomb at runtime?

public IQueryable<Story> FindAllStories(){
var stories = (from s in db.Stories
orderby s.DateEntered descending
select new Story
{
Title = s.Title,
UserName = s.aspnet_User.UserName
}
);
return stories;
}
I need to pass this as IQueryable so the pagination helper I found online can only pull the items I need. The problem is that at runtime when the helper tries to do source.Count(); the compiler isn't a happy camper because it's an 'explicit contruction of an entity type query'.
How would I alter this LINQ to SQL method so this does not happen?
Also, to help me grasp this, why does the previous code not work and this one does?
public IQueryable<Story> FindAllStories(){
var stories = (from s in db.Stories
orderby s.DateEntered descending
select s);
return stories;
}
Update
I'm beginning to think the way to accomplish this (verified it works) is to create a POCO called UserStory. The new class has 2 properties: one of type Story and the other string UserName. I can then return an IQueryable of UserStory without a problem.
That's great; however, I still don't get why that method would work and my other doesn't. The other is adding a property of string UserName to my partial class Story and passing that object between layers. What's the difference?
The following link is to a blog post tat describes an issue similar to yours. It seems like the solution was to return a type that inherets from Story instead of a Story type:
http://devlicio.us/blogs/derik_whittaker/archive/2008/04/25/linq2sql-explicit-construction-of-entity-exception.aspx
Hope this helps.

How can I clone (or copy) a object to another one, but don't copy PK attribute?

I'm trying to copy all object attibutes to another object, fox example:
Person p1 = new Person();
p1.name = "John";
p1.sex = 'M';
Person p2 = new Person();
p2 = Util.Clone(p1);
The problem is that Person entity has an identity PK 'codPerson' and I don't want to copy this PK. Is there a way to clone/copy an object, but don't copy its PK attribute??
Thanks!!!
Perhaps you might consider the following:
Ensure Util.Clone(Person p) doesn't
copy the codPerson attribute
Clear the attribute after the Clone method
is called
Create a new Person object while specifically initializing specific properties.
At the most basic level you can't - given an arbitrary object o (and the question implies you're looking for generic solutions) you have no way to determine which field is a primary key.
So you step up a level - by adding some constraints i.e. that you will inform your tools what the primary key field is (or fields are) and hence enable use of a generic method.
So, you could explicitly specify the PK field (name) to the code that does the clone (I assume that you're using reflection to avoid explicitly copying all the fields). You could identify the PK by using annotation of some sort on the classes being cloned and have the clone code exclude properties with the relevant annotation (the annotation implies that the field won't be cloned). There may be other methods
You mention Linq - are you using a specific bit of Linq ?
Beyond that there's not a lot one can suggest without more details - ah but the question is tagged with Linq to SQL (which I missed) ok...
There's nothing obvious in a Linq to SQL class that will help - nor with the "table" but a quick look at the generated code in .designer.cs shows that a key field has annotations similar to the following (taken from a set of classes I have to hand):
[Column(Storage="_ID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
Therefore when you're do your reflection on the class to enumerate the properties to copy you'll want to look for the column and the "IsPrimaryKey" property within the column - unfortunately the details of how to do that are some distance outside my comfort zone!
You could manually set the properties on the new object to be equal to the old one.
For example:
Person p2 = new Person {
Name = p1.Name,
Gender = p1.Gender,
//...
};
you can use .net Reflection.
//using System.Reflection;
var yourEntity = new Person {Name = "Green", Surname= "White"};
var cloneEntity = new Person();
var allPi = typeof(Person).GetProperties();
foreach (var pi in allPi)
{
if (pi.Name != "codPerson" && pi != null && pi.CanWrite)
pi.SetValue(cloneEntity , pi.GetValue(yourEntity , null));
}