Problems translating an SQL query to LINQ in VS Lightswitch - sql

So, I am having an issue with an SQL query that is translated LINQ (and works - tested), but that same LINQ query does not work in Lightswitch. Of course I did not expect to work straight out, but I am struggling to properly convert it.
So here is a image of the tables that I base my query on:
http://dl.dropbox.com/u/46287356/tables.PNG
(sorry for outside link, but not enough rep points :))
The SQL query is the following:
SELECT WorkingUnits.Name AS WUName, ContractPositions.WUInstanceId,
Materials.Cost, BillingValues.Value, BillingValues.PricePerUnit
FROM WorkingUnits
INNER JOIN
Materials ON WorkingUnits.Id = Materials.Material_WorkingUnit
INNER JOIN ContractPositions ON
Materials.Id = ContractPositions.ContractPosition_Material
INNER JOIN BillingValues ON
ContractPositions.Id = BillingValues.BillingValue_ContractPosition
Now, I have transformed this to LINQ in the following way:
var query = from wu in this.DataWorkspace.ApplicationData.WorkingUnits
join m in this.DataWorkspace.ApplicationData.Materials on
new { Id = WorkingUnits.Id } equals new { Id = m.Material_WorkingUnit }
join cp in this.DataWorkspace.ApplicationData.ContractPositions on
new { Id = m.Id } equals new { Id = cp.ContractPosition_Material }
join bv in this.DataWorkspace.ApplicationData.BillingValues on
new { Id = cp.Id } equals new { Id = bv.BillingValue_ContractPosition }
select new
{
usage = bv.Value * bv.PricePerUnit,
totalCost = (bv.Value * bv.PricePerUnit) * m.Cost,
amount = (bv.Value*bv.PricePerUnit) * m.Cost / wu.WUPrice
};
Notice that I have changed a few things - like section of colums, as I do not need that in Lightswitch.
So while this works agains the SQL server, Lightswitch complains that I must consider explicitly specifying the type of the range variable 'WorkingUnits'.
I tried to cast it, but then there are other errors such as:
'int' does not contain a definition for 'Id' and no extension method 'Id'
accepting a first argument of type 'int' could be found (are you missing
a using directive or an assembly reference?)
So my questions is, how do I properly convert that query and expect it to work?
Also, If we take that my database is setup correctly, do I even need to use 'joins' in the LINQ?
Any ideas are appreciated!

try something like this
var query = from wu in this.DataWorkspace.ApplicationData.WorkingUnits
join m in this.DataWorkspace.ApplicationData.Materials on
wu.Id equals m.WorkingUnitID }
join cp in this.DataWorkspace.ApplicationData.ContractPositions on
m.Id equals cp.ContractPosition_Material
join bv in this.DataWorkspace.ApplicationData.BillingValues on
cp.Id equals bv.BillingValue_ContractPosition
select new
{
usage = bv.Value * bv.PricePerUnit,
totalCost = (bv.Value * bv.PricePerUnit) * m.Cost,
amount = (bv.Value*bv.PricePerUnit) * m.Cost / wu.WUPrice
};

What about starting at the bottom (BillingValues) and working your way up using the entity references?
eg
var query = from bv in this.DataWorkspace.ApplicationData.BillingValues
let m = bv.ContractPosition.Material
let wu = m.WorkingUnit
select new
{
usage = bv.Value * bv.PricePerUnit,
totalCost = (bv.Value * bv.PricePerUnit) * m.Cost,
amount = (bv.Value*bv.PricePerUnit) * m.Cost / wu.WUPrice
};

Related

API return breaks when using inner join?

I've been building an API which was working absolutely fine until I tried to add an inner join
The SQL I'm passing returns as I'd expect when I run it in Beaver (am using Mac)
However, when I try and access it via my API end point, instead of the combined results, I get only results from the table I added in the join
I presume am doing something really stupid ...
From my controller:
result = dbtest.FromDatabase(
"SELECT A.FAMILY_ID
,A.START_TIME
,A.END_TIME
,A.WORKER_ID
,A.WEEK_NO
,A.ID
,A.SHIFT_NO
,A.DAY_OF_WEEK
,A.HOLIDAY_OR_TERM
,B.WORKER_NAME
FROM SHIFT_REQ_TBL A
INNER JOIN WORKER_TBL B ON A.WORKER_ID = B.WORKER_ID
WHERE A.FAMILY_ID = '" + FAMILY.FAMILY_ID + "'");
From my model
if (query.Contains("SHIFT_REQ_TBL"))
{
var tbl_type = new TimekeeperTables.SHIFT_REQ_TBL();
tbl_type.FAMILY_ID = Convert.ToInt32(reader["FAMILY_ID"]);
tbl_type.ID = Convert.ToInt32(reader["ID"]);
tbl_type.WEEK_NO = Convert.ToInt32(reader["WEEK_NO"]);
tbl_type.WORKER_ID = Convert.ToInt32(reader["WORKER_ID"]);
tbl_type.SHIFT_NO = reader["SHIFT_NO"].ToString();
tbl_type.START_TIME = reader["START_TIME"].ToString();
tbl_type.END_TIME = reader["END_TIME"].ToString();
tbl_type.DAY_OF_WEEK = reader["DAY_OF_WEEK"].ToString();
tbl_type.HOLIDAY_OR_TERM = reader["HOLIDAY_OR_TERM"].ToString();
tbl_type.WORKER_NAME = reader["WORKER_NAME"].ToString();
db_results.Add(tbl_type);
jsonDoc = JsonConvert.SerializeObject(db_results);
}
Results (from postman)
"[{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"}]"
I'm sorry, I was being stupid and figured it out
I was using a really dumb way of identifying the calling URL which already was checking for SHIFT_TBL in an earlier case

Linq-to-sql query error with StartsWith

I am using LINQPad to run the following query:
var pds = (from p in Projects
group p by p.FiscalYearVariables.FiscalYear into grouped
where grouped.Count() > 0
select new {
fiscalYear = grouped.Key,
projectDetails = grouped.SelectMany(a=>a.ProjectDetails),
Programs = (from pwbs in Programs.SelectMany(a =>a.ProgramWbsNumbers)
let ds = pwbs.WbsNumbers.DisplayString
where pwbs.Programs.IsActive
&& (from w in WbsNumbers
where w.DisplayString.StartsWith(ds)
select w).Any()
select pwbs.Programs)
});
pds.Dump();
And I get the error:
NotSupportedException: Only arguments that can be evaluated on the client are supported for the String.StartsWith method.
I'm not sure how to go about correcting this error. I need to get each Program where the WbsNumber starts with the WbsNumber contained within ProgramWbsNumbers if that helps.
Try this:
where SqlMethods.Like(w.DisplayString, ds + "%")

New at EntityFramework how do I write this query in linq to entity

SELECT i.answerc, d.answera, d.answerb
FROM tableone AS u
INNER JOIN tabletwo AS i ON u.OneId = i.OneId
INNER JOIN tablethree AS l ON u.TwoId = l.TwoId
INNER JOIN tablefour AS k ON u.ThreeId = k.ThreeId
INNER JOIN tablefive AS p ON u.fourId = p.fourId
INNER JOIN tablesix AS d ON u.fiveId = d.fiveId
WHERE u.columnone = #parameter_one
AND l.columnone = #parameter_two
AND k.columnone = #parameter_three
AND p.columnone = #Parameter_four
I have the Model created, but how I would write this query is confusing me right now? Can anybody help me out here? I had this working using old school SqlConnection but I've been told to port it to the Entity Framework 4.
If your tables are not in relations than there is no navigation properties in .edmx file. So your LINQ should be something like this :
var query = from u in context.tableone
join i in context.tabletwo on u.OneId equals i.OneId
join l in context.tablethree on u.TwoId equals l.TwoId
join k in context.tablefour on u.ThreeId equals k.ThreeId
join p in context.tablefive on u.fourId equals p.fourId
join d in context.tablesix on u.fiveId equals d.fiveId
where u.columnone == parameterONE
&& l.columnone == parameterTWO
&& k.columnone == parameterTHREE
&& p.columnone == parameterFOUR
select new { i.answerc, d.answera, d.answerb };
Where parameterXXX are variables.
But if you have navigation properties, you should use them!
If you're using an .edmx file, you can look at the designer and see what your navigation properties are. Instead of thinking in straight sql, think more in how you would build the object which you're trying to extract from the database based on your .edmx's navigation properties.
If you have the relationships set up, I guess you can do something like this.
var query = from u in context.tableone
where u.columnone == parameterONE
&& u.tablethree.columnone == parameterTWO
&& u.tablefour.columnone == parameterTHREE
&& u.tablefive.columnone == parameterFOUR
select new { u.tabletwo.answerc, u.tablesix.answera, u.tablesix.answerb };

How do I map lists of nested objects with Dapper

I'm currently using Entity Framework for my db access but want to have a look at Dapper. I have classes like this:
public class Course{
public string Title{get;set;}
public IList<Location> Locations {get;set;}
...
}
public class Location{
public string Name {get;set;}
...
}
So one course can be taught at several locations. Entity Framework does the mapping for me so my Course object is populated with a list of locations. How would I go about this with Dapper, is it even possible or do I have to do it in several query steps?
Alternatively, you can use one query with a lookup:
var lookup = new Dictionary<int, Course>();
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course))
lookup.Add(c.Id, course = c);
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(l); /* Add locations to course */
return course;
}).AsQueryable();
var resultList = lookup.Values;
See here https://www.tritac.com/blog/dappernet-by-example/
Dapper is not a full blown ORM it does not handle magic generation of queries and such.
For your particular example the following would probably work:
Grab the courses:
var courses = cnn.Query<Course>("select * from Courses where Category = 1 Order by CreationDate");
Grab the relevant mapping:
var mappings = cnn.Query<CourseLocation>(
"select * from CourseLocations where CourseId in #Ids",
new {Ids = courses.Select(c => c.Id).Distinct()});
Grab the relevant locations
var locations = cnn.Query<Location>(
"select * from Locations where Id in #Ids",
new {Ids = mappings.Select(m => m.LocationId).Distinct()}
);
Map it all up
Leaving this to the reader, you create a few maps and iterate through your courses populating with the locations.
Caveat the in trick will work if you have less than 2100 lookups (Sql Server), if you have more you probably want to amend the query to select * from CourseLocations where CourseId in (select Id from Courses ... ) if that is the case you may as well yank all the results in one go using QueryMultiple
No need for lookup Dictionary
var coursesWithLocations =
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (course, location) => {
course.Locations = course.Locations ?? new List<Location>();
course.Locations.Add(location);
return course;
}).AsQueryable();
I know I'm really late to this, but there is another option. You can use QueryMultiple here. Something like this:
var results = cnn.QueryMultiple(#"
SELECT *
FROM Courses
WHERE Category = 1
ORDER BY CreationDate
;
SELECT A.*
,B.CourseId
FROM Locations A
INNER JOIN CourseLocations B
ON A.LocationId = B.LocationId
INNER JOIN Course C
ON B.CourseId = B.CourseId
AND C.Category = 1
");
var courses = results.Read<Course>();
var locations = results.Read<Location>(); //(Location will have that extra CourseId on it for the next part)
foreach (var course in courses) {
course.Locations = locations.Where(a => a.CourseId == course.CourseId).ToList();
}
Sorry to be late to the party (like always). For me, it's easier to use a Dictionary, like Jeroen K did, in terms of performance and readability. Also, to avoid header multiplication across locations, I use Distinct() to remove potential dups:
string query = #"SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id";
using (SqlConnection conn = DB.getConnection())
{
conn.Open();
var courseDictionary = new Dictionary<Guid, Course>();
var list = conn.Query<Course, Location, Course>(
query,
(course, location) =>
{
if (!courseDictionary.TryGetValue(course.Id, out Course courseEntry))
{
courseEntry = course;
courseEntry.Locations = courseEntry.Locations ?? new List<Location>();
courseDictionary.Add(courseEntry.Id, courseEntry);
}
courseEntry.Locations.Add(location);
return courseEntry;
},
splitOn: "Id")
.Distinct()
.ToList();
return list;
}
Something is missing. If you do not specify each field from Locations in the SQL query, the object Location cannot be filled. Take a look:
var lookup = new Dictionary<int, Course>()
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.Name, l.otherField, l.secondField
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course)) {
lookup.Add(c.Id, course = c);
}
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(a);
return course;
},
).AsQueryable();
var resultList = lookup.Values;
Using l.* in the query, I had the list of locations but without data.
Not sure if anybody needs it, but I have dynamic version of it without Model for quick & flexible coding.
var lookup = new Dictionary<int, dynamic>();
conn.Query<dynamic, dynamic, dynamic>(#"
SELECT A.*, B.*
FROM Client A
INNER JOIN Instance B ON A.ClientID = B.ClientID
", (A, B) => {
// If dict has no key, allocate new obj
// with another level of array
if (!lookup.ContainsKey(A.ClientID)) {
lookup[A.ClientID] = new {
ClientID = A.ClientID,
ClientName = A.Name,
Instances = new List<dynamic>()
};
}
// Add each instance
lookup[A.ClientID].Instances.Add(new {
InstanceName = B.Name,
BaseURL = B.BaseURL,
WebAppPath = B.WebAppPath
});
return lookup[A.ClientID];
}, splitOn: "ClientID,InstanceID").AsQueryable();
var resultList = lookup.Values;
return resultList;
There is another approach using the JSON result. Even though the accepted answer and others are well explained, I just thought about an another approach to get the result.
Create a stored procedure or a select qry to return the result in json format. then Deserialize the the result object to required class format. please go through the sample code.
using (var db = connection.OpenConnection())
{
var results = await db.QueryAsync("your_sp_name",..);
var result = results.FirstOrDefault();
string Json = result?.your_result_json_row;
if (!string.IsNullOrEmpty(Json))
{
List<Course> Courses= JsonConvert.DeserializeObject<List<Course>>(Json);
}
//map to your custom class and dto then return the result
}
This is an another thought process. Please review the same.

nhibernate hql with named parameter

I have implemented a search function using Castel Active Record. I thought the code is simple enough but I kept getting
NHibernate.QueryParameterException : could not locate named parameter [searchKeyWords]
errors. Can someone tell me what went wrong? Thanks a million.
public List<Seller> GetSellersWithEmail(string searchKeyWords)
{
if (string.IsNullOrEmpty(searchKeyWords))
{
return new List<Seller>();
}
string hql = #"select distinct s
from Seller s
where s.Deleted = false
and ( s.Email like '%:searchKeyWords%')";
SimpleQuery<Seller> q = new SimpleQuery<Seller>(hql);
q.SetParameter("searchKeyWords", searchKeyWords);
return q.Execute().ToList();
}
Why do not u pass the % character with parameter?
string hql = #"select distinct s
from Seller s
where s.Deleted = false
and ( s.Email like :searchKeyWords)";
SimpleQuery<Seller> q = new SimpleQuery<Seller>(hql);
q.SetParameter("searchKeyWords", "%"+searchKeyWords+"%");
return q.Execute().ToList();