using String.Split in a LINQ Where clause not working - c#

I have below LINQ query:
List<string> paths = (from an in modelDb.MyTable
where an.Id == 0 &&
an.fileName.Split('_')[0] == "19292929383833abe838ac393" &&
an.fileName.Split('_')[1].ToUpper() == "FINANCE" &&
an.fileName.ToUpper().Contains("SIGNED")
select an.filePath).ToList();
... which is throwing below error on run-time:
Entity Framework 'ArrayIndex' is not supported in LINQ to Entities
fileName field in the LINQ query is a column in MyTable of string data type and contains strings like:
8845abd344ejk3444_FINANCE_SIGNED.pdf
4565abd34ryjk3454_FINANCE_UNSIGNED.pdf
477474jkedf34dfe4_MARKETING_UNSIGNED.pdf
and so on...

As stays in the message split will be not supported in the linq to sql and if you want to get result without additional methods, using Contains() should give you required result
List<string> paths = (from an in modelDb.MyTable
where an.Id == 0 &&
an.fileName.Contains("19292929383833abe838ac393") &&
an.fileName.Contains("FINANCE") &&
an.fileName.ToUpper().Contains("SIGNED")
select an.filePath).ToList();
Another way is to load all in memory and do split after that, e.g.
List<string> temp = (from an in modelDb.MyTable
where an.Id == 0
an.fileName.ToUpper().Contains("SIGNED")
select an.filePath).ToList();
paths = temp.Where(an =>
an.fileName.Split('_')[0] == "19292929383833abe838ac393" &&
an.fileName.Split('_')[1].ToUpper() == "FINANCE").ToList();

Related

How to create dynamic Multiple And linq conditions in foreach loop

if (rowCount == 1)
{
query =
(from x in partJoinTableRepository.GetPartJoinQuery()
join y in partRepository.GetPartsQuery() on x.PartId equals y.Id
join z in partProductTypeReposiotry.GetPartProductTypesQuery() on x.PartId equals z.PartId
where y.IsSkipped == 0 && (y.IsDisabled != "Y" || y.IsDisabled == null) && z.CreatedDate == x.CreatedDate
&& x.CreatedDate == Convert.ToDateTime(fromDate) && cpaclassids.Contains(x.ProductTypeId.ToString())
select x).Cast<PartJoinTable>().AsQueryable();
predicate = PredicateBuilder.True(query);
}
else
{
query = query.Join(partJoinTableRepository.GetPartJoinQuery(), "PartID", "PartID", "inner", "row1", null).Cast<PartJoinTable>().AsQueryable();
// predicate = PredicateBuilder.True(query);
} //query contains multiple dynamic inner joins
//repids contains the list ,I used the predicate builder for the linq to create AND Queries
foreach(var item in repids)
{
predicate = PredicateBuilder.True(query);
if (typeid == "3")
{
predicate = predicate.And(z => ids.Contains(z.ProductTypeId.ToString()) &&
z.CreatedDate == Convert.ToDateTime(fromDate));
}
}
var count = query.Where(predicate).Distinct().Count();
the above line is taking long time to execute,ids contains the lists and query contains the linq query.basically I need to form a multiple "AND" conditions
//The Query is taking lot of time to execute and multiple and conditions are not working
Remove ToList to improve performance. Because ToList execute your query and retrieve object list to memory. But you need only count. you don't need objects.
var count = query.Where(predicate).Distinct().Count();
If I understood you right, your problem is that this query has a long running time. Let's see your code in the last line:
var count = query.Where(predicate).Distinct().ToList().Count();
In LINQ to SQL (and to entities), your query doesn't execute thought you use ToList(), ToArray() etc.. For example, consider the following query:
var strings = Db.Table
.Where((string s) => s.Contains("A")) // Will convert to something like WHERE s LIKE '%A%'
.Select(s => s.ToUpper()) // Will convert to something like SELECT upper(s)
.ToList(); // Here the query sends to the DB and executes
The final query is SELECT upper(s) FROM [Table] WHERE s LIKE '%A%'.
In you case, first you send the query to the DB and get all the objects corresponding to the condition (.Where()), and then get their count inside your app.
Instead, if you'll get from the DB only the count, the query will be faster:
var count = query.Where(predicate).Distinct().Count(); // No .ToList()! Here, .Count() executes the query.

Unable to form the proper Linq query using an "IN" list

I have the below SQL Query
;with cte as(
select a.*
from [dbo].[AccountViewModel] a
where a.COLLECTORID = 724852
and a.MONTH = 12
and a.YEAR=2015)
select *
from cte c
where c.DispCode in ('Deceased','DND','WN','WI','NC','NORESPONSE','SKIP','SHIFTED','SFU')
OR (c.DispCode in('PTP','DIB','WCE','DP') and convert(varchar(11), c.PTPDate) >=convert(varchar(11), getdate()))
OR (MONTH(c.LastPaymentDate) = 12 and YEAR(c.LastPaymentDate)=2015)
I need to convert this into an equivalent Linq query (C#).
The Cte part is working fine with the below program (I have cross checked the records)
private List<AccountViewModel> GetAllAcountsForLoggedInAgents()
{
var allAcountsForLoggedInAgents = new List<AccountViewModel>();
allAcountsForLoggedInAgents = new ViewModelDatabase()
.Accounts
.Where(a =>
a.COLLECTORID == 724852 &&
a.MONTH == DateTime.Now.Month &&
a.YEAR == DateTime.Now.Year
)
.ToList();
return allAcountsForLoggedInAgents;
}
However the part outside CTE is not working correctly (means improper records)
GetAllAcountsForLoggedInAgents()
.Where
(
a =>
("Deceased,DND,WN,WI,NC,NORESPONSE,SKIP,SHIFTED,SFU".Split(',').Any(x => x.Contains(a.DispCode)))
|| ("PTP,DIB,WCE,DP".Split(',').Any(b => b.Contains(a.DispCode)) && a.PTPDate >= DateTime.Now)
|| (a.LastPaymentDate.Value.Month == 12 && a.LastPaymentDate.Value.Year == 2015)
)
I believe that may be I am using "ANY" in a wrong way.
This condition is not the same as the IN clause
("Deceased,DND,WN,WI,NC,NORESPONSE,SKIP,SHIFTED,SFU".Split(',').Any(x => x.Contains(a.DispCode)))
because it searches a.DispCode in one of the strings. You should use equality instead:
("Deceased,DND,WN,WI,NC,NORESPONSE,SKIP,SHIFTED,SFU".Split(',').Any(x => x == a.DispCode))
This is not ideal, because Split operation is not free, so you don't want to do it as part of your query. Making a static array of strings:
static readonly string[] DispCodeFilter = new string[] {
"Deceased", "DND", "WN", "WI", "NC", "NORESPONSE", "SKIP", "SHIFTED", "SFU"
};
...
(DispCodeFilter.Any(x => x == a.DispCode))
Your In condition is incorrect. It can be fixed by adding an extension method. I am using a generic method, but you could make it type specific if you only need/want it for strings. I am using params, so you can either provide the items one by one or via a split.
public static bool In<T>(this T item, params T[] items) {
return items.Any(i=> Equals(item, i));
}
GetAllAcountsForLoggedInAgents().Where( a => a.DispCode.In
("Deceased","DND","WN","WI","NC","NORESPONSE","SKIP","SHIFTED","SFU")
|| (a.DispCode.In("PTP,DIB,WCE,DP".Split(',')) &&
a.PTPDate >= DateTime.Now)
|| (a.LastPaymentDate.Value.Month == 12 && a.LastPaymentDate.Value.Year == 2015)
)
One difference between this and the sql version, and a reason you may not want it to be generic, is that it is case sensitive: "wi" doesn't equal "WI".
Here are 2 simple rules for converting SQL to Linq
SQL Linq
============ ==========
IN (...) Contains
EXISTS (...) Any
where Contains is the corresponding Enumerable/Queryable method (not to be mixed with string.Contains).
According to this, your Linq criteria should be something like this
var DispCodes1 = new [] { "Deceased", "DND", "WN", "WI", "NC", "NORESPONSE", "SKIP", "SHIFTED", "SFU" };
var DispCodes2 = new [] { "PTP", "DIB", "WCE", "DP" };
GetAllAcountsForLoggedInAgents()
.Where
(
a =>
DispCodes1.Contains(a.DispCode)
|| (DispCodes2.Contains(a.DispCode)) && a.PTPDate >= DateTime.Now)
|| (a.LastPaymentDate.Value.Month == 12 && a.LastPaymentDate.Value.Year == 2015)
)
dasblinkenlight answer contains a good point, so you can make DispCodes1 and DispCodes2 static, but that's not essential.
Another thing to mention is that the way you did the "CTE part" is not equivalent to the SQL query, where cte is just a named subquery and the whole query executes in the database, while in your implementation the cte part is executed in the database, then gets materialized in the memory and the additional query is executed in the memory using Linq To Objects. To make it fully equivalent and let the whole query execute in the database, change the GetAllAcountsForLoggedInAgents result type to IQueryable<AccountViewModel> and remove ToList call.

Using Linq to filter by List<ListItem>

I am trying to extend my linq query with additional search criteria to filter the data by sending also a List<Listitem> to the function for processing. The List can contain 1 or more items and the objective is to retreive all items which match any criteria.
Since i am sending several search criteria to the function the goal is to make a more accurate filter result the more information i am sending to the filter. If one or several criterias are empty then the filter will get less accurate results.
Exception is raised every time i execute following code, and I cant figure out how to solve the using statement to include the List<ListItem>. Appreciate all the help in advance!
Exception: Unable to create a constant value of type 'System.Web.UI.WebControls.ListItem'. Only primitive types or enumeration types are supported in this context.
using (var db = new DL.ENTS())
{
List<DL.PRODUCTS> products =
(from a in db.PRODUCTS
where (description == null || description == "" ||
a.DESCRIPTION.Contains(description)) &&
(active == null || active == "" || a.ACTIVE.Equals(active, StringComparison.CurrentCultureIgnoreCase)) &&
(mID == null || mID == "" || a.MEDIA_ID == mID) &&
(mID == null || objTypes.Any(s => s.Value == a.OBJECTS)) //Exception here!
select a).ToList<DL.PRODUCTS>();
return products;
}
Pass collection of primitive values to expression:
using (var db = new DL.ENTS())
{
var values = objTypes.Select(s => s.Value).ToArray();
List<DL.PRODUCTS> products =
(from a in db.PRODUCTS
where (description == null || description == "" || a.DESCRIPTION.Contains(description)) &&
(active == null || active == "" || a.ACTIVE.Equals(active, StringComparison.CurrentCultureIgnoreCase)) &&
(mID == null || mID == "" || a.MEDIA_ID == mID) &&
(mID == null || values.Contains(a.OBJECTS))
select a).ToList<DL.PRODUCTS>();
return products;
}
That will generate SQL IN clause.
Note - you can use lambda syntax to compose query by adding filters based on some conditions:
var products = db.PRODUCTS;
if (!String.IsNullOrEmpty(description))
products = products.Where(p => p.DESCRIPTION.Contains(description));
if (!String.IsNullOrEmpty(active))
products = products.Where(p => p.ACTIVE.Equals(active, StringComparison.CurrentCultureIgnoreCase)));
if (!String.IsNullOrEmpty(mID))
products = products.Where(p => p.MEDIA_ID == mID);
if (mID != null)
products = products.Where(p => values.Contains(p.OBJECTS));
return products.ToList();
Linq isn't able to convert the predicate on ListItem to something useful to Sql.
I would suggest that you pre-project the values of the ListItems into a simple List<string> before using this with Contains (which is converted to IN)
var listValues = objTypes.Select(_ => _.Value).ToList();
List<DL.PRODUCTS> products = ...
listValues.Contains(a.OBJECTS))

Proper use of StartWith and Contains in Entity Framework

I want to remove prefixes from StoreName field in Linq Linq to Entity query.
I have following query which gets list of all prefixes in table
Query1
var _prefix = context.Prefixes.Select(pre => pre.Prefix1);
I want to use this result in Query2
Query2
var objRetailer = from stores in context.RetailerStoredtls
join ret in context.RetailerContactdtls
on stores.RetailerID equals ret.RetailerID
join retreg in context.RetailerRegDates
on stores.RetailerID equals retreg.RetailerRegDateId
where (stores.IsDeleted == null || stores.IsDeleted == false)
&& (stores.CreatedDate.Value.Year == iYear || stores.ModifiedDate.Value.Year == iYear)
&& retreg.IsApproved== true
orderby stores.StoreName
select new
{
stores.StoreID,
Store = stores.StoreName,
Area = stores.StoreCity,
Zip = stores.StoreZip,
SellingCard = (storessellingcard.Contains(stores.RetailerID.Value) ? true : false)
StoreWithoutPrefix = stores.StoreName.StartsWith(<one of prefix retrieved from Query1> ? stores.StoreName : <stores.StoreName without prefix>
};
Unfortunately we have .StartWith() that take only string parameter not result of Query1, if I go with .Contains it will not check for whether it starts with or not, it just check whether string is present there. What should I do to accomplish this task?
Thanks.
You can use IndexOf, but probably you have problems with case sensitivity. So add a parameter CompareOptions.IgnoreCase

LINQ Syntax for Current Revision

How can I write this SQL statement using C# and LINQ? I am quering an Oracle database and the table has multiple revisions of the records. Therefore, I want onyl the current revision of each record contained in the table.
The SQL looks like this:
select TP_ID, TP_TEXT, TP_DEFN_SAKEY
from TP_DEFN tp1
where tp1.TP_ACTIVE_FLAG = 'Y' and
tp1.FAMILY_ID = 1 and
tp1.TP_DEFN_REV_DTS = (select max(TP_DEFN_REV_DTS)
from TP_DEFN tp2
where tp2.family_id = tp1.family_id and tp2.tp_id = tp1.tp_id )
order by TP_ID
TP_DEFN_REV_DTS is the date time field that stores the current revision.
I am a beginner with LINQ and have been struggling to find an workable solution. Every time that I try grouping in the LINQ query I get an error
GroupBy is not supported
Try something like this:
var res =
from tp1 in TP_DEFN
where tp1.TP_ACTIVE_FLAG == "Y" &&
tp1.FAMILY_ID == 1 &&
tp1.TP_DEFN_REV_DTS == (from tp2 in TP_DEFN
where tp2.FAMILY_ID == tp1.FAMILY_ID &&
tp2.TP_ID == tp1.TP_ID
select tp2.TP_DEFN_REV_DTS).Max()
orderby tp1.TP_ID
select new
{
tp1.TP_ID,
tp1.TP_TEXT,
tp1.TP_DEFN_SAKEY
};
Off the top of my head, and not knowing which LINQ provider you're using...
var q = from tp1 in Context.TP_DEFN
where tp1.TP_ACTIVE_FLAG == "Y"
&& tp1.FAMILY_ID == 1
&& tp1.TP_DEFN_REV_DTS
== Context.TP_DEFN.Where(tp2 => tp2.FAMILY_ID == tp1.FAMILY_ID
&& tp2.TP_ID == tp1.TP_ID)
.Max(tp2 => tp2.TP_DEFN_REV_DTS)
orderby tp1.TP_ID
select new
{
tp1.TP_ID,
tp1.TP_TEXT,
tp1.TP_DEFN_SAKEY
};
If you're using entity framework or linq-to-sql, you can just pass the direct sql if you want (although that'll prevent change tracking, at least by default).
For EF, use ObjectContext.ExecuteStoreQuery: http://msdn.microsoft.com/en-us/library/dd487208.aspx
For L2S, use DataContext.ExecuteQuery: http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.executequery.aspx

Categories