I'm working out this LINQ query in LINQPad. I'm doing something like this:
var perms = (from x in db.TableName // etc
select new ProfilePermission
{ // etc
});
perms = perms.Concat(from x in db.TableName // etc
select new ProfilePermission
{ // etc
});
var results = (from pi in db.AnotherTable
where pi.IsActive
select new MyViewModel
{
KeyId = pi.Id,
Permissions = (from pm in perms
where pi.ChildId == pm.ChildId
select pm)
}
Using this sub query works. So, I figured, let's move it to an extension method. I tried doing this:
public static IQueryable<ProfilePermission> GetProfilePermissions
(
this IMkpContext db
)
{
var perms = (from x in db.TableName // etc
select new ProfilePermission
{ // etc
});
perms = perms.Concat(from x in db.TableName // etc
select new ProfilePermission
{ // etc
});
return perms;
}
var results = (from pi in db.AnotherTable
where pi.IsActive
select new MyViewModel
{
KeyId = pi.Id,
Permissions = (from pm in db.GetProfilePermissions()
where pi.ChildId == pm.ChildId
select pm)
}
Now I get a message:
NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[PublicationSystem.Model.ViewModels.ProfilePermission] GetProfilePermissions(PublicationSystem.Model.IMkpContext)' method, and this method cannot be translated into a store expression.
Why does the sub query work one way, and not the other? I thought that perms ended up as an IQueryable<> either way.
The difference is that at the place you've used the extension method, it's not executed, but becomes a part of another expression (Select in your case), i.e. is memorized as MethodCallExpression to a method that is not recognized by EF query provider.
If you indeed use the extension method in some top level query construct like Join or GroupJoin, it will work. Or if you can move the call outside the query and store the result into a variable.
For instance, in your case the following will work:
var results =
from pi in db.AnotherTable
where pi.IsActive
join pm in db.GetProfilePermissions() on pi.ChildId equals pm.ChildId into permissions
select new MyViewModel
{
KeyId = pi.Id,
Permissions = permissions
};
as well as this:
var permissions = db.GetProfilePermissions();
var results =
from pi in db.AnotherTable
where pi.IsActive
select new MyViewModel
{
KeyId = pi.Id,
Permissions = (from pm in permissions
where pi.ChildId == pm.ChildId
select pm)
};
Related
I am developing my first MVC application with Entity Framework. I have a table USERS and a table RESTRICTIONS
In my controller I wrote a function that returns the number of common restrictions between two users:
public int common_restrictions(int id1, int id2)
{
MyModel bd = new MyModel();
int count= 0;
var restrictions = from c in bd.RESTRICTIONS where c.ID_USER == id1
select c;
var restrictions2 = from c in bd.RESTRICTIONS where c.ID_USER == id2
select c;
foreach (var prop in restrictions)
{
var nr = restrictions2.Count(p => p.ID_PROP == prop.ID_PROP);
if (nr != 0)
{
count++;
}
}
return count;
}
The function works as it supposed to.
Now in another function in the same controller I want to sort the list of users in descending order of their common restrictions with a specific user (let's say user with the id 12). I got the list of users in a query but I don't know how to sort them after that
var query = from u in bd.USERS where u.ID != 12
select u;
// sort the list??
I've tried
var query = from u in bd.USERS orderby(common_restrictions(u.ID,
12)) select u;
but I get the following error message:
"LINQ to Entities does not recognize the method 'Int32 common_restrictions (Int32, Int32)' method, and this method cannot be translated into a store expression."
You need to do this in two steps if you don't want to include another property to your User object. And I think this following way is easier.
var query = (from u in bd.USERS where u.ID != 12
select u).ToList();
var unSorteUsers = (from u in query
select new
{
User = u,
CR = common_restrictions(u.ID,12)
});
var sortedUsers = (from u in unSorteUsers
orderby u.CR
select new User
{
ID = u.User.ID,
//All other properties.
});
I have a method like below:
public void GetUserIdByCode(string userCode)
{
var query = from u in db.Users
where u.Code == userCode // userCode = "LRAZAK"
select u.Id;
var userId = query.FirstOrDefault(); // userId = 0 :(
}
When I ran the code, I got the default value of 0 assigned to userId meaning the Id was not found.
However, if I changed the userCode with a string like below, I will get the value I want.
public void GetUserIdByCode(string userCode)
{
var query = from u in db.Users
where u.Code == "LRAZAK" // Hard-coded string into the query
select u.Id;
var userId = query.FirstOrDefault(); // userId = 123 Happy days!!
}
My question is why passing the parameter into the LINQ query does not work?
When I stepped into the code, I got the SQL statement like so:
// Does not work...
{SELECT "Extent1"."LOGONNO" AS "LOGONNO"FROM "DEBTORSLIVE"."DEBTORS_LOGONS" "Extent1"WHERE ("Extent1"."LOGONCODE" = :p__linq__0)}
The hard-coded LINQ query (the working one) gives an SQL statement as below:
// Working just fine
{SELECT "Extent1"."LOGONNO" AS "LOGONNO"FROM "DEBTORSLIVE"."DEBTORS_LOGONS" "Extent1"WHERE ('LRAZAK' = "Extent1"."LOGONCODE")}
What would be the solution?
As a work-around, I use Dynamic Linq.
The code below is working for me.
public void GetUserIdByCode(string userCode)
{
string clause = String.Format("Code=\"{0}\"", userCode);
var userId = db.Users
.Where(clause)
.Select(u => u.Id)
.FirstOrDefault();
}
The database query returns an object of User with Code and Id as properties. This is defined in one of my classes.
Here is syntax that will work to pass an argument to a LINQ query.
Not sure how many people will be searching this topic so many years later, but here's a code example that gets the job done:
string cuties = "777";
// string collection
IList<string> stringList = new List<string>() {
"eg. 1",
"ie LAMBDA",
"777"
};
var result = from s in stringList
where (s == cuties)
select s;
foreach (var str in result)
{
Console.WriteLine(str); // Output: "777"
}
What's the equivalent LINQ instruction for a Datatable of the following SQL query:
SELECT code_direction, count(TP) AS CN
FROM table1
WHERE cod_time = 'A011'
GROUP BY TP,code_direction;
and how to get the result into a new datatable?
I tried to convert it but I there're some errors. Someone could take a look on this:
var query = from t in table1.AsEnumerable()
group t by new { t.TP, t.code_direction }
into grp
select new
{
grp.Key.code_direction,
CN = grp.Count(t.TP)
};
foreach (var x in query)
{
Console.Write(x.code_direction);
Console.Write(x.CN);
}
As far as your first question goes. The LINQ equivalent of the SQL query is:
var query = from t in table1.AsEnumerable()
where t.cod_time == "A011"
group t by new { t.TP, t.code_direction }
into grp
select new
{
grp.Key.code_direction,
CN = grp.Count()
};
Note that you don't have to pass any argument to grp.Count(). (For the obvious reason that in SQL COUNT(TP) is the same as COUNT(*), i.e. just count the number of rows. The story would be different if you'd use COUNT(DISTINCT TP) or similar.)
As far as the second question goes, if your query just returned an IEnumerable<T> where T is DataRow (i.e. a query like table1.AsEnumerable().Where(r => r.cod_time == "A011")) then you could just the DataTableExtensions.CopyToDataTable extension method. As your query returns an anonymous type however, you will have to follow these instructions found on MSDN.
I Have been using LINQ to work on a JSON object returned from a remote sharepoint web service. I have posted this because most of the answers I found online were slightly different from what I needed.
a json list of daily activities is returned from a remote sharepoint list & is then summarised using LINQ
The simplified version of a custom object definition is shown below( & which is defined in the models area of an MVC application)
public class MyCustomObjectList
{
public string eventdate { get; set; }
public string userid { get; set; }
public string action { get; set; }
}
The JSON object is serialised into a MyCustomObjectList array.
var customobject = serializer.Deserialize<MyCustomObjectList>(jsonobject);
I wanted to work out how many actions of each type happened on a given day. NB eventdate is stored as a string in format yyyy-mm-dd hh:MM:ss. This was to simplify conversions between c#, JSON & Jquery ( where required I create DateTime objects elsewhere in the code using the
eventdate.
Some will argue this is inefficient, but I prefer to split processes into a sequential set of really simple operations, for the sake of easier debugging & to help other people follow my code. Thats why there are 2 Linq queries .
querya strips out the time component from the eventdate This ensures our later grouping happens by day, & not by second. To be doubly sure that there is no caching, I create it in a new field called actionday. I also rename action to activity, because intellisense was getting confused!! The other columns are copied as is.
var querya =
from c in customobject.rows
select new { actionday = c.eventdate.Substring(0, 10), activity = c.action, c.userid,
c.eventdate };
/* queryb produces a grouped count of querya, grouped on actionday & activity, creating new columns actionkey,ActionCount,Dte,action & DetailList ( which is a summary for debugging purposes)
*/
var queryb=
from p in querya group p by new { p.actionday, p.activity} into idGroup
actionkey = idGroup.Key,
ActionCount = idGroup.Count(),
Dte = idGroup.Key.actionday,
action = idGroup.Key.activity,
DetailList = idGroup
};
Here’s a version that sumarises by 3 columns
var queryc = from p in querya
group p by new { p.actionday, p.userid, p.activity} into idGroup
select new
{
actionday = idGroup.Key,
ActionCount = idGroup.Count(),
userid = idGroup.Key.userid,
Dte = idGroup.Key.actionday,
action = idGroup.Key.activity,
DetailList = idGroup
};
I have a linq query from two database, however, each time the program stops at the query point. I don't know how to debug linq using VS. Can someone help me figure it out what's wrong here? Thank you.
public List<Promotion> GetBroder(string source)
{
string _connString = ConfigurationManager.AppSettings["DB1"];
PromotionDataContext dc = new PromotionDataContext(_connString);
string connString = ConfigurationManager.AppSettings["DB2"];
ReachDirectDataContext RDdc = new ReachDirectDataContext(connString);
return (from b in RDdc.BrokerNos
from p in dc.Promotions
where p.Source == source && p.Broker == b.BrokerNo1
select new Promotion() {Code=p.Code,BrokerName=b.Name}).ToList<Promotion>();
}
Your linq statement looks fine. To aid in debugging, I find it helpful to assign the linq query to a local variable, then return the local variable. You can then set a breakpoint on the return statement, and when the debugger stops at the breakpoint, you can inspect the query local variable to see what's in it, interactively. You can use the Locals window in VS, or the Immediate Window to surf around inside your app's variables and see what's going on.
In particular, double check that the inputs into your linq query are actually providing data. Verify that RDdc.Brokernos is non-empty, and dc.Promotions, etc. If these are empty, the result will be empty. Track your bug "upstream".
Minor point: You don't need to specify the type parameter on the .ToList() call in the select. The compiler will infer the type automagically.
You can use the following to display the generated SQL for the Linq statement.
ReachDirectDataContext RDdc = new ReachDirectDataContext(connString);
RDdc.Log = Console.Out;
return (from b in RDdc.BrokerNos
from p in dc.Promotions
where p.Source == source && p.Broker == b.BrokerNo1
select new Promotion() {Code=p.Code,BrokerName=b.Name}).ToList<Promotion>();
You can try the following to separate out the queries.
var promotions = from p in dc.Promotions
where p.Source == source
select p;
var brokers = from o in promotions
join b in RDdc.BrokerNos on o.Broker equals b.BrokerNo1
select new Promotion
{
Code = o.Code,
BrokerName = b.Name
};
return brokers.ToList();
The double from looks suspicious to me.
(This is not the equivalent to SQL's JOIN statement, if that is what you were aiming for.)
If you can combine the contexts:
Try creating relationship between BrokerNos and Promotions in edmx and using navigation property in query.
For example:
var result = dc.Promotions.Where(p => p.Source == source).
Select(p => new Promotion() {
Code = p.Code,
BrokerName = p.Broker.Name, // use new navigation property here
});
If not (intersection will be done in memory, not on DB!!!:
var result1 = dc.Promotions.Where(p => p.Source == source).
Select(p => new Promotion() {
Code = p.Code,
BrokerId = p.BrokerId, // add id property for intermediate results
}).ToList();
var result2 = RDdc.Brokers.ToList();
var finalResult = result1.Where(p => result2.Contains(b => b.BrokerId == p.BrokerId)).Select(p => new Promotion{
Code = p.Code,
BrokerName = result2.Single(b => b.BrokerId == p.BrokerId).Name,
});
I have a database table that contains an nvarchar column like this:
1|12.6|18|19
I have a Business Object that has a Decimal[] property.
My LINQ Query looks like this:
var temp = from r in db.SomeTable select new BusinessObject {
// Other BusinessObject Properties snipped as they are straight 1:1
MeterValues = r.MeterValues.Split('|').Select(Decimal.Parse).ToArray()
};
var result = temp.ToArray();
This throws an NotSupportedException: Method 'System.String[] Split(Char[])' has no supported translation to SQL.
That kinda sucks :) Is there any way I can do this without having to add a string property to the business object or selecting an anonymous type and then iterating through it?
My current "solution" is:
var temp = from r in db.SomeTable select new {
mv = r.MeterValues,
bo = new BusinessObject { // all the other fields }
};
var result = new List<BusinessObject>();
foreach(var t in temp) {
var bo = t.bo;
bo.MeterValues = t.mv.Split('|').Select(Decimal.Parse).ToArray();
result.Add(bo);
}
return result.ToArray(); // The Method returns BusinessObject[]
That's kinda ugly though, with that temporary list.
I've tried adding a let mv = r.MeterValues.Split('|').Select(Decimal.Parse).ToArray() but that essentially leads to the same NotSupportedException.
This is .net 3.5SP1 if that matters.
You need to force the select clause to run on the client by calling .AsEnumerable() first:
var result = db.SomeTable.AsEnumerable().Select(r => new BusinessObject {
...
MeterValues = r.MeterValues.Split('|').Select(Decimal.Parse).ToArray()
}).ToList();
You can't use split, but in this scenario you can do the following:
// Database value is 1|12.6|18|19
string valueToFind = "19";
var temp = from r in db.SomeTable.Where(r => ("|" + r.MeterValues + "|").Contains("|" + valueToFind + "|"));
This code adds outer pipes (|) to the database value on the fly inside the query so you can do start, middle, and end value matches on the string.
For example, the above code looks for "|19|" inside "|1|12.6|18|19|", which is found and valid. This will work for any other valueToFind.
You don't need to use a temporary list:
var query = from r in db.SomeTable
select new
{
r.Id,
r.Name,
r.MeterValues,
...
};
var temp = from x in query.AsEnumerable()
select new BusinessObject
{
Id = x.Id,
Name = x.Name,
MeterValues = x.mv.Split('|').Select(Decimal.Parse).ToArray(),
...
};
return temp.ToArray();
Unfortunately its the IQueryable you are using (Linq to SQL) that is not supporting the Split function.
You are really only left with the IEnumerable (Linq to Objects) support for it in this case. You second code snippet is what you need to do, or something like...
var temp = (from r in db.SomeTable select new {
mv = r.MeterValues,
bo = new BusinessObject { // all the other fields }
}).AsEnumerable().Select(blah, blah) ;