Multiple where statements in Entity Framework - c#

Database structure sample:
Department Table
-DepartmentID
Facility Table
-Facility ID
-DepartmentID (FK)
-Block
-Level
-Name
-etc
I am trying to select from database from the EF using two where clause. I am not sure what went wrong at the where clause. I am stuck at it. Please help and advice. I have googled on the internet but cannot find the solution to it.
string departmentID = "SIT";
string block = "L";
string level = "4";
string name = "L.425";
using (var db = new KioskContext())
{
var facilitys = from f in db.Facilitys
Where clause to select departmentID where equals to SIT and also where any block or level or name contains any alphabets. Please advice how should i write the statement with two where clause. Thank You!
where f.Department.DepartmentID == departmentID
&& (f.Block.Contains("%" + block + "%") || f.Level.Contains("%" + level + "%")
|| f.Name.Contains("%" + name + "%"))
Remaining of the query statement to select all the facilities
orderby f.FacilityID
select new
{
f.FacilityID,
f.DepartmentID,
f.Description,
f.Block,
f.Level,
f.Name,
f.OpenHours,
f.CloseHours,
f.MaxBkTime,
f.MaxBkUnits,
f.MinBkTime,
f.MinBkUnits
};
foreach (var fac in facilitys)
{
FacObject facobject = new FacObject(fac.FacilityID, fac.DepartmentID, fac.Description, fac.Block, fac.Level,
fac.Name, fac.OpenHours, fac.CloseHours, fac.MaxBkTime, fac.MaxBkUnits, fac.MinBkTime, fac.MinBkUnits);
sqlFacList.Add(facobject);
}
}

Remove the "%" from the various Contains clauses, they are SQL cruft you do not need.
where f.Department.DepartmentID == departmentID
&& (f.Block.Contains(block)
|| f.Level.Contains(level)
|| f.Name.Contains(name ))
Remember LINQ is not just for SQL!

Related

String concatenation in join clause

I have been having some issue with adding some string concatenation in my LINQ JOIN condition and its translation in Oracle sql query. This used to work fine on our legacy solution that was using EF.
Let's take the following example:
var query = (from sub in connection.InvoiceSubStatuses
join lab in connection.Litteral
on "STN_ASP_PIE_STATUT_EXTERNE[" + sub.StatusCode + "]" + "{" + vendorId + "}" equals lab.Code
where sub.IsFlaggedDelete == 0
select new InvoiceSubStatusDTO
{
Description = lab.Libelle,
StatusCode = sub.StatusCode,
Id = sub.Id,
Culture = lab.Culture,
StatusType = sub.StatusType
});
This is interpreted as the following sql code:
SELECT "l".LIBELLE "Description", "s".CODE_STATUSFAC "StatusCode", "s".SYS_ID "Id", "l".CULTURE "Culture", "s".TYPE_STATUSFAC "StatusType"
FROM STATUSFAC "s"
INNER JOIN LITTERAUX "l" ON N'STN_ASP_PIE_STATUT_EXTERNE[' || COALESCE("s".CODE_STATUSFAC, N'') || N']' || N'{' || TO_NCHAR(:p__vendorId_0) || N'}' = "l".CODE
WHERE ("s".DIGITAL_LETTER = 1) AND ("s".SYS_FLAG_DEL = 0)
This causes an error , as Oracle doesn't allow 'N' text literal inside COALESCE ( COALESCE("s".CODE_STATUSFAC, N'')). If I manually change it to COALESCE("s".CODE_STATUSFAC, ''), the query works fine, but I don't know how to resolve this from Linq.
Has anyone encountered this issue and found a workaround?Thanks
So the issue was coming from the fact that the column in the DB in non-nullable, so when trying to do a string concatenation, the Oracle provider adds the COALESCE . So in order to bypass this , I added the attribute [Required] on the column StatusCode .

Is there an easier way to make this query?

I have two different tables from a DataBase named "empleados" and "fichajes". empleados has the employees data and fichajes has the date and time from when they started working.
I want to get the total work time done by a specific employee between two dates, lets say from 20th to 29th.
I have this query which I use with Dapper on C#:
SELECT CONCAT(e.nombre, " " ,e.apellido) as fullName, tfichajes.total
FROM empleados e
INNER JOIN (
SELECT f1.nif,
SEC_TO_TIME(SUM(TIME_TO_SEC(f1.fechasalida) - TIME_TO_SEC(f1.fechaentrada))) AS total
FROM fichajes f1
WHERE f1.fechasalida <= '2019-04-29'
and f1.fechaentrada >= '2019-04-20'
GROUP BY f1.nif
) AS tfichajes
ON e.nif = tfichajes.nif
WHERE e.nif = '33333333P'
This works just fine, but I was wondering if it was possible to make it simpler.
This is the code I have in my program:
public static List<string> CalculaTotalHoras(string nif, DateTime fechaEntrada, DateTime fechaSalida)
{
var dbCon = DBConnection.Instancia();
if (dbCon.Conectado())
{
string format = "yyyy-MM-dd";
List<string> result = new List<string>();
using (IDbConnection conexion = dbCon.Conexion)
{
var output = conexion.Query($"SELECT CONCAT(e.nombre, \" \" ,e.apellido) as fullName, tfichajes.total " +
$"FROM empleados e INNER JOIN (SELECT f1.nif, SEC_TO_TIME(SUM(TIME_TO_SEC(f1.fechasalida) - TIME_TO_SEC(f1.fechaentrada))) AS total " +
$"FROM fichajes f1 where f1.fechasalida <= '{fechaSalida.ToString(format)}' and f1.fechaentrada >= '{fechaEntrada.ToString(format)}' GROUP BY f1.nif) " +
$"as tfichajes ON e.nif = tfichajes.nif where e.nif = '{ nif }';").ToList();
var i = 0;
foreach (IDictionary<string, object> row in output)
{
foreach (var pair in row)
{
if (i == 0)
{
result.Add(pair.Value.ToString());
i++;
}
else
{
result.Add(pair.Value.ToString());
}
}
}
return result;
}
}
else return null;
}
If you have problems with the readability of the code here you have a gyazo.
Workingtime fichajes table and employees empleados table.
With that exact query, the expected results are Alvaro Arguelles 00:05:00, and in the code, I want to get Alvaro Arguelles and 00:05:00 separated in the result List.
Did I made it much harder than it actually is?
Your query can be significantly simplified. Your inner query is selecting ALL employees working in the date in question, I would start with the INNER query as the main FROM table getting the data for the one employee. THEN join to the employee to grab the name.
select
CONCAT(e.nombre, " " ,e.apellido) as fullName,
OneEmp.Total
from
( select
f1.nif,
SEC_TO_TIME( SUM(TIME_TO_SEC(f1.fechasalida)
- TIME_TO_SEC(f1.fechaentrada))) AS total
from
fichajes f1
where
f1.nif = '33333333P'
AND f1.fechasalida >= '2019-04-20'
AND f1.fechasalida <= '2019-04-29' ) OneEmp
JOIN empleados e
on OneEmp.nif = e.nif
Since your query was summarizing per the one employee, it would only return a single row anyhow. So the above query will return 1 row, 2 columns... name and total. Should be able to read those two values back directly.
But as others commented, PARAMETERIZE your query calls.

Combining Two Different DbContexts (LINQ) from Separate Databases

I am getting the error:
The specified LINQ expression contains references to queries that are associated with different contexts.
because I'm trying to combine two different DbContexts, each from separate databases.
I was trying to convert this SQL:
SELECT orderformdump.itemno,basedescription,info,upc,CAST(UNITPRICE AS DECIMAL(18,2)),caseqty, sum(qty) AS userquantity
FROM [DataWarehouse].[dbo].[ORDERFORMDUMP]
LEFT JOIN PPPLTD.dbo.ICPRICP ON replace([DataWarehouse].[dbo].[ORDERFORMDUMP].[ITEMNO],'-','') = ICPRICP.ITEMNO
LEFT JOIN PPPLTD.dbo.WEBITEMINFO ON ORDERFORMDUMP.ITEMNO = WEBITEMINFO.ITEMNO
LEFT JOIN pppltd.dbo.weboeordh ON [user] = #username
LEFT JOIN pppltd.dbo.weboeordd ON pppltd.dbo.WEBOEORDD.ITEMNO = REPLACE(datawarehouse.dbo.ORDERFORMDUMP.ITEMNO,'-','') and weboeordd.ORDUNIQ = weboeordh.orduniq
Left JOIN pppltd.dbo.weboeordsubmit ON weboeordsubmit.orduniq = weboeordd.ORDUNIQ and weboeordd.ORDUNIQ != weboeordsubmit.orduniq
LEFT JOIN PPPLTD.dbo.WEBLOGINACCESS ON WEBLOGINACCESS.[USER] = #username
LEFT JOIN PPPLTD.dbo.ARCUS ON ARCUS.IDCUST = WEBLOGINACCESS.CUSTID
where (allowinbc = 'Yes' or allowinab = 'Yes')
AND [PRICELIST] = ARCUS.PRICLIST
and [CURRENCY] = 'CDN' and DPRICETYPE = 1
and (itemgroup like '%' + #search + '%' or itemtype like '%' + #search + '%' or itembrand like '%' + #search + '%'
or subcat like '%' + #search + '%' or orderformdump.description like '%' + #search + '%' or basedescription like '%'+ #search + '%'
or orderformdump.ITEMNO like '%'+#search+'%' or UPC like '%'+#search+'%' or (select top 1 1 from pppltd.dbo.ICITEMO where OPTFIELD like 'UPC%' and VALUE like '%'+#search+'%'
and ITEMNO = DataWarehouse.dbo.ORDERFORMDUMP.itemno) is not null)
group by ORDERFORMDUMP.ITEMNO,BASEDESCRIPTION,info,UPC,CAST(UNITPRICE AS DECIMAL(18,2)),caseqty
order by basedescription
to LINQ:
var qty = db.WebOEOrdD.Sum(d => d.QTY);
var unitPrice = db.IcPricP.Select(p => decimal.Round(p.UNITPRICE, 2));
var query = from item in dbData.OrderFormDump
join icp in db.IcPricP on item.ITEMNO.Replace("-", "") equals icp.ITEMNO
join itemInfo in db.WebItemInfo on item.ITEMNO equals itemInfo.ITEMNO
join weboeordh in db.WebOEOrdH on username equals weboeordh.USER
join weboeordd in db.WebOEOrdD on new { itemno = item.ITEMNO.Replace("-", ""), orduniq = weboeordh.ORDUNIQ } equals new { itemno = weboeordd.ITEMNO, orduniq = weboeordd.ORDUNIQ }
join weboeordsubmit in db.WebOEOrdSubmit on weboeordd.ORDUNIQ equals weboeordsubmit.ORDUNIQ where weboeordsubmit.ORDUNIQ != weboeordd.ORDUNIQ
join webloginaccess in db.WebLoginAccess on username equals webloginaccess.USER
join arcus in db.Arcus on webloginaccess.CUSTID equals arcus.IDCUST
where (item.ALLOWINBC == "Yes" && item.ALLOWINAB == "Yes")
&& icp.PRICELIST == arcus.PRICLIST
&& icp.CURRENCY == "CDN" && icp.DPRICETYPE == 1
&& (item.BASEDESCRIPTION.Contains(searchword) || item.DESCRIPTION.Contains(searchword) || item.CATEGORY.Contains(searchword) || item.FOODACCSPEC.Contains(searchword) || item.ITEMBRAND.Contains(searchword) || item.ITEMGROUP.Contains(searchword) || item.ITEMNO.Contains(searchword) || item.ITEMSUBTYPE.Contains(searchword) || item.ITEMTYPE.Contains(searchword) || itemInfo.INFO.Contains(searchword) || item.UPC.Contains(searchword) || item.UPC.Substring(2, 10).Contains(searchword))
orderby item.BASEDESCRIPTION
group item by new { item.ITEMNO, item.BASEDESCRIPTION, itemInfo.INFO, item.UPC, unitPrice, item.CASEQTY, qty } into items
select new { items.Key.ITEMNO, items.Key.BASEDESCRIPTION, items.Key.INFO, items.Key.UPC, unitPrice, items.Key.CASEQTY, qty };
Am I converting it correctly? Do I have to create two separate queries and combine them? Lastly, can I combine two different databases into one dbContext? If so, how do I do this?
Thank you.
UPDATE
I tried adding tables from another database into my main dbContext and I'm getting this error:
The entity type ORDERFORMDUMP is not part of the model for the current context.
UPDATE #2
What I did to solve my problem doesn't really answer this question, however it is a good workaround.
What I did was I took the only table in my query that was part of a different DbContext and another database (ORDERFORMDUMP), and I copied that table to the main database in SQL Server. As a result, I was able to add that table to my main DbContext.
There are a couple ways of accomplishing this.
I would recommending creating a view of your query, then bind it to a new linq object, and then you can query off of that. Trying to write your query fully in linq is possible, but it would just get nasty, given you are working with multiple data contexts.
You are going to have to have two different queries, materialize the query results, then do whatever operations you want to do on them in memory.
What I did to solve my problem doesn't really answer this question, however it is a good workaround.
What I did was I took the only table in my query that was part of a different DbContext and another database (ORDERFORMDUMP), and I copied that table to the main database in SQL Server. As a result, I was able to add that table to my main DbContext.

Using SQL instead of LINQ in Telerik Open Access

I'm using Telerik Open Access. I have two separate projects that have Open Access data and then a third project that has the bulk of my code. I've been working on a way to convert a simple (at least I thought it was) SQL query to LINQ so that I can get the data I need. I have not been successful. I've had to break a single LINQ query into separate queries, because of the need for the Trim() function (I think). This has led to a lengthy piece of code and I'm still not getting the same results as my SQL query.
So my question is, is there anyway to use SQL instead of LINQ to access the data in the Open Access projects? If so, can you show me the syntax to do that for my query?
If it is not possible to use SQL, can you show me show me the proper way to convert my SQL query into LINQ so that I get the same results?
Thank you.
My SQL query is
SELECT DISTINCT us2.ccustno, us2.dispname, us2.csiteno, so.s1_name
FROM [DALubeDeacom].[dbo].[dmbill] bi
INNER JOIN [DALubeDeacom].[dbo].[dmso1] so
ON bi.bi_s1id = so.s1_id
INNER JOIN [DALubeNew].[dbo].[usersecurity] us2
ON so.s1_name = us2.cparentno
WHERE
us2.ctype = 'JOBSITE'
AND us2.csiteno is not null
AND us2.csiteno != ''
AND bi.bi_smid = '22'
ORDER BY us2.csiteno
My LINQ query is
public List<DataModelSample> GetLocationsBySalesNo(string salesNo)
{
int iSalesNo = int.Parse(salesNo.Trim());
try
{
var dmso = (
from so in deacom.Dmso1
join qt in deacom.Dmbills
on so.S1_id equals qt.Bi_s1id
where qt.Bi_smid == iSalesNo
select new Dmso1
{
S1_id = so.S1_id
, S1_name = so.S1_name.Trim()
}
);
var usec = (
from us in dbContext.Usersecurities
where us.Cparentno != null && us.Cparentno.Trim() != "" && us.Ctype.Trim() == "JOBSITE" && us.Csiteno.Trim() != ""
select new Usersecurity
{
Ccustno = us.Ccustno.Trim(),
Csiteno = us.Csiteno.Trim(),
Dispname = us.Dispname.Trim(),
Cparentno = us.Cparentno.Trim()
}
);
var customers =
(
from us in usec
join so in dmso
on us.Cparentno equals so.S1_name
select us
);
customers = customers.GroupBy(x => x.Csiteno).Select(x => x.First());
List<DataModelSample> listLocations =
(
from c in customers
select new DataModelSample
{
customerID = c.Ccustno
,
origLocationName = c.Csiteno + " " + c.Dispname
,
origLocationID = c.Csiteno
}
).OrderBy(x => x.origLocationID).ToList();
return listLocations.ToList();
}
catch (Exception ex)
{
throw ex;
}
} // GetLocationsBySalesNo(userInfo.csalesno)
Edit 1 - 2-19-16
Tried a suggestion by ViktorZ. His query was similar to the one I first tried. It returned the error "Identifier 'Ctype' is not a parameter or variable or field of 'DALube_DeacomModel.Dmbill'. If 'Ctype' is a property please add the FieldAlias or Storage attribute to it or declare it as a field's alias." From an online search, it looked like this was do to "extended fields". I don't seemed to be using such fields. The only way I could get around this error was to break it into the smaller LINQ queries in my original question, which didn't produce the right results. Any suggestions?
Here's the code:
var query = (from bill in deacom.Dmbills
join so in deacom.Dmso1 on bill.Bi_s1id equals so.S1_id
join us in dbContext.Usersecurities on so.S1_name equals us.Cparentno
where us.Ctype == "JOBSITE"
&& us.Csiteno != null
&& us.Csiteno != string.Empty
&& bill.Bi_smid == iSalesNo
select new
{
ccustno = us.Ccustno.Trim(),
dispname = us.Dispname.Trim(),
csiteno = us.Csiteno.Trim(),
s1_name = so.S1_name.Trim()
}).Distinct();
One very crude approximation of your SQL query is:
var query = (from bill in deacom.Bills
join so in deacom.LubeDeacom on bill.bi_s1id equals so.s1_id
join us in deacom.UserSecurity on so.s1_name equals us.cparentno
where us.ctype = "JOBSITE"
&& us.csiteno != null
&& us.csiteno != string.Empty
&& bill.smid = '22'
order by us.csiteno
select new
{
us.ccustno.Trim(),
us.dispname.Trim(),
us.csiteno.Trim(),
so.s1_name.Trim()
}).Distinct();
// to check the translation result
string sql = query.ToString()
// to get the results
var result = query.ToList()
If this is not working for you, you can always fall back to Telerik Data Access ADO.NET API. Here is a documentation article how to use it.

SqlClient data filtering

From this database, I want to select two users, 321 and 102 where BOOK_TYPE is 1 or 2 and compare their BOOK_NR, if they coincide then save BOOK_COUNT only from USER_ID 102.
SqlCommand command = new SqlCommand("SELECT BOOK_NR, BOOK_COUNT, USER_ID, BOOK_TYPE " +
"FROM BOOKS " +
"WHERE (BOOK_TYPE = '1' OR" + "BOOK_TYPE = '2')" +
"AND MONTH(DATE) = '" + DateTime.Today.Month + "'" +
"GROUP BY BOOK_NR, BOOK_COUNT, USER_ID, BOOK_TYPE ", BookConn);
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
string BookNr = (string)reader[0];
int Count = (int)reader[1];
int User = (int)reader[2];
int Type = (int)reader[3];
if( " HERE I NEED HELP " )
{
" AND HERE :) "
}
}
reader.Close();BookConn.Close();
My only solution is as follows
List<string> User321List = new List<string>();
List<string> User102List = new List<string>();
if(User == 321 && Type == 1){ User321List.Add(BookNr.Trim());}
if(User == 102 && Type == 2){ User102List.Add(BookNr.Trim()+"\t"+Count);}
and then...
int count = 0;
foreach (string x in User321List)
{
foreach (string y in User102List)
{
List<string> part = y.Split('\t').Select(p => p.Trim()).ToList();
var z = string.Compare(part[0], x);
if (z == 0)
{
count += int.Parse(part[1]);
}
}
}
but it takes a lot of time to get result..
if someone figured something out of my nightmare please help and sorry for my bad english...
I'm going to focus on answering just the first part:
I want to select two users, 321 and 102 where BOOK_TYPE is 1 or 2 and compare their BOOK_NR, if they coincide then save BOOK_COUNT only from USER_ID 102.
You want to join the table to itself:
SELECT b1.BOOK_NR, b1.BOOK_COUNT, b1.USER_ID, b1.BOOK_TYPE
FROM BOOKS b1
INNER JOIN BOOKS b2 ON b2.BOOK_TYPE IN ('1','2')
AND b2.User_ID = 321
AND b1.BOOK_NR = b2.BOOK_NR
WHERE b1.User_ID = 102 AND b1.BOOK_TYPE IN ('1','2')
AND MONTH(b1.DATE) = MONTH(current_timestamp)
AND MONTH(b2.DATE) = MONTH(current_timestamp)
Since one of your concerns was performance -- that the query was too slow -- I also need to ask whether the BOOK_TYPE column is a string or a number. If it's a number column, you should omit the single quotes on the values: for example, just use IN (1,2) instead of IN ('1','2'). This will help performance by avoiding potential per-row conversions and by potentially better matching to indexes.
If your query is slow, consider splitting the operation in two. Create a SQL Table function so you don't repeat the SQL, passing the users, type and month in as parameters.
Do not do an IN (as suggested in comment). Keep in mind that an OR in the WHERE clause will result in no indexes being used.
Join the two instances of the table function and let sql do the matching for you.
e.g.
Select * from dbo.UserList('321',1,12) as u321
inner join dbo.UserList('102',2,12) as u102
on u321.field = u102.field
BTW... I covered quite a few advanced topics, but the idea is to let SQL do what it does best: JOINS
As a matter of interest, if your sql table function returns many rows, and you ever use them in a sp, remember to use the option (recompile) clause ...

Categories