Linq return string array - c#

/// <summary>
/// Returns list of popular searches
/// </summary>
public static string[] getPopularSearches(int SectionID, int MaxToFetch)
{
using (MainContext db = new MainContext())
{
return (from c in db.tblSearches where c.SectionID == SectionID && c.Featured select new[] { c.Term });
}
}
I looked at other questions but they seem to be slightly different, I get the error:
Cannot implicitly convert type 'System.Linq.IQueryable<string[]>' to 'string[]'
I know this is probably simple, could someone point out what's wrong here please?

Sure - you're trying to return from a method declared to return a string[], but you're returning a query - which isn't a string in itself. The simplest way of converting a query to an array is to call the ToArray extension method.
However, as you're already selecting a string array for every element in the query, that would actually return string[][]. I suspect you really want to select a single string per query element, and then convert the whole thing into an array, i.e. code like this:
public static string[] GetPopularSearches(int sectionID, int maxToFetch)
{
using (MainContext db = new MainContext())
{
var query = from c in db.tblSearches
where c.SectionID == sectionID && c.Featured
select c.Term;
return query.Take(maxToFetch)
.ToArray();
}
}
Note that:
I've renamed the method and parameters to match .NET naming conventions
I've added a call to Take in order to use the maxToFetch parameter

You are attempting to return an unmaterialized query. The query is only evaluated when it is enumerated. Luckily for you, the ToArray method take the pain out of enumerating and storing. Simply adding it to the end of your query should fix everything.
return (
from c in db.tblSearches
where c.SectionID == SectionID && c.Featured
select new[] { c.Term }
).ToArray();
EDIT
Looking in more detail, perhaps:
return (
from c in db.tblSearches
where c.SectionID == SectionID && c.Featured
select new[] { c.Term }
).SelectMany(x => x).ToArray();
to flatten the results of your query, or even (less redundantly):
return (
from c in db.tblSearches
where c.SectionID == SectionID && c.Featured
select c.Term
).ToArray();

Add .ToArray() at the end of the return statement.

Related

LINQ sentence, check if remain elements

We're new in LINQ and we want to know if the are any method to check if there are elements remaining in the sentence like the ResultSet.next() from Java.
In Java the resultSet return true if there are elements remaining and false if not. We want to know if the is a method like that in LINQ.
public List<Product> FindProductsByKeyword(string productName, Category category, int page, int size)
{
DbSet<Product> products = Context.Set<Product>();
List<Product> result;
if (category == null)
{
result = (from p in products
where p.productName.Contains(productName) //, StringComparison.OrdinalIgnoreCase)
orderby p.productName descending
select p).Skip(page).Take(size).Include("Category").ToList();
}
else
{
result = (from p in products
where p.productName.Contains(productName)
&& p.categoryId == category.id
orderby p.productName descending
select p).Skip(page).Take(size).ToList();
}
return result;
}
This our code( I dont know if this will help) we take elements with a number of Size but we dont know if there are more elements.
Using the terminating method ToList() initiates a DB query and parses the returned table as a simple in-memory List<T> (same as ArrayList<T> in Java).
If you want to check if there are any entries in the result, you can just check the list's Count property:
if (result.Count > 0)
{
// Do something
}
Or use LINQ's Any() extension method, which is a bit more readable in this context:
if (result.Any())
{
// Do something
}
If you returned an Enumerable, then you could use Enumerable.MoveNext()
For example:
var p = products.AsEnumerable();
var i = p.GetEnumerator();
while( i.MoveNext())
i.Current.productfieldname.Dump() // LinqPad .Dump() shows value

Linq to Entities Where clause compare value that can be int or string

I have a drop down list that will provide either a numeric or the word ANY. I need to create a LINQ SELECT containing a WHERE clause that can mimic the following SQL:
var p varchar2(3);
select ... from ...
where (
( (:p = 'ANY') and id in (select distinct id from Ids) )
or
(:p='1' and id = 42)
)
ps: I will be using an expression tree to handle the OR aspect :-)
Somthing like this?
string input = /***/
var result = Context.Entities
.Where(ent => (input == "ANY"
&& Context.UserIds.Select(usr => isr.Id)
.Distinct()
.Contains(ent.Id))
|| (input == "1" && ent.Id == 42))
.Select(ent => /***/);
Disclaimer: written from memory, can contain compile-time errors (typo mistakes etc)

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.

Custom function in Entity Framework query sometimes translates properly, sometimes doesn't

I have this function:
public static IQueryable<Article> WhereArticleIsLive(this IQueryable<Article> q)
{
return q.Where(x =>
x != null
&& DateTime.UtcNow >= x.PublishTime
&& x.IsPublished
&& !x.IsDeleted);
}
And it works just fine in this query:
from a in Articles.WhereArticleIsLive()
where a.Id == 5
select new { a.Title }
But it doesn't work in this only slightly more complex query:
from s in Series
from a in Articles.WhereArticleIsLive()
where s.Id == a.SeriesId
select new { s, a }
I get this error message:
NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IQueryable1[TheFraser.Data.Articles.Article] WhereArticleIsLive(System.Linq.IQueryable1[TheFraser.Data.Articles.Article])' method, and this method cannot be translated into a store expression.
Any idea why? Is there another way to consolidate query parameters like this?
Thanks in advance.
EDIT: corrections by Craig.
I'm leaving this here, because I think it's a valuable tool: Use linqkit! But not for solving this question though :-)
Instead of returning IQueryable, use Expression to factor out predicates. E.g. you could define the following static method on Article:
public static Expression<Func<Article,bool>> IsLive()
{
return x =>
x != null
&& DateTime.UtcNow >= x.PublishTime
&& x.IsPublished
&& !x.IsDeleted
}
Then, ensure to store a reference to this expression when building your query, something along the lines of (not tested):
var isLive = Article.IsLive();
from s in Series
from a in Articles.Where(isLive)
where s.Id == a.SeriesId
select new { s, a }

How can I conditionally apply a Linq operator?

We're working on a Log Viewer. The use will have the option to filter by user, severity, etc. In the Sql days I'd add to the query string, but I want to do it with Linq. How can I conditionally add where-clauses?
if you want to only filter if certain criteria is passed, do something like this
var logs = from log in context.Logs
select log;
if (filterBySeverity)
logs = logs.Where(p => p.Severity == severity);
if (filterByUser)
logs = logs.Where(p => p.User == user);
Doing so this way will allow your Expression tree to be exactly what you want. That way the SQL created will be exactly what you need and nothing less.
If you need to filter base on a List / Array use the following:
public List<Data> GetData(List<string> Numbers, List<string> Letters)
{
if (Numbers == null)
Numbers = new List<string>();
if (Letters == null)
Letters = new List<string>();
var q = from d in database.table
where (Numbers.Count == 0 || Numbers.Contains(d.Number))
where (Letters.Count == 0 || Letters.Contains(d.Letter))
select new Data
{
Number = d.Number,
Letter = d.Letter,
};
return q.ToList();
}
I ended using an answer similar to Daren's, but with an IQueryable interface:
IQueryable<Log> matches = m_Locator.Logs;
// Users filter
if (usersFilter)
matches = matches.Where(l => l.UserName == comboBoxUsers.Text);
// Severity filter
if (severityFilter)
matches = matches.Where(l => l.Severity == comboBoxSeverity.Text);
Logs = (from log in matches
orderby log.EventTime descending
select log).ToList();
That builds up the query before hitting the database. The command won't run until .ToList() at the end.
I solved this with an extension method to allow LINQ to be conditionally enabled in the middle of a fluent expression. This removes the need to break up the expression with if statements.
.If() extension method:
public static IQueryable<TSource> If<TSource>(
this IQueryable<TSource> source,
bool condition,
Func<IQueryable<TSource>, IQueryable<TSource>> branch)
{
return condition ? branch(source) : source;
}
This allows you to do this:
return context.Logs
.If(filterBySeverity, q => q.Where(p => p.Severity == severity))
.If(filterByUser, q => q.Where(p => p.User == user))
.ToList();
Here's also an IEnumerable<T> version which will handle most other LINQ expressions:
public static IEnumerable<TSource> If<TSource>(
this IEnumerable<TSource> source,
bool condition,
Func<IEnumerable<TSource>, IEnumerable<TSource>> branch)
{
return condition ? branch(source) : source;
}
When it comes to conditional linq, I am very fond of the filters and pipes pattern.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/
Basically you create an extension method for each filter case that takes in the IQueryable and a parameter.
public static IQueryable<Type> HasID(this IQueryable<Type> query, long? id)
{
return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query;
}
Doing this:
bool lastNameSearch = true/false; // depending if they want to search by last name,
having this in the where statement:
where (lastNameSearch && name.LastNameSearch == "smith")
means that when the final query is created, if lastNameSearch is false the query will completely omit any SQL for the last name search.
Another option would be to use something like the PredicateBuilder discussed here.
It allows you to write code like the following:
var newKids = Product.ContainsInDescription ("BlackBerry", "iPhone");
var classics = Product.ContainsInDescription ("Nokia", "Ericsson")
.And (Product.IsSelling());
var query = from p in Data.Products.Where (newKids.Or (classics))
select p;
Note that I've only got this to work with Linq 2 SQL. EntityFramework does not implement Expression.Invoke, which is required for this method to work. I have a question regarding this issue here.
It isn't the prettiest thing but you can use a lambda expression and pass your conditions optionally. In TSQL I do a lot of the following to make parameters optional:
WHERE Field = #FieldVar OR #FieldVar IS NULL
You could duplicate the same style with a the following lambda (an example of checking authentication):
MyDataContext db = new MyDataContext();
void RunQuery(string param1, string param2, int? param3){
Func checkUser = user =>
((param1.Length > 0)? user.Param1 == param1 : 1 == 1) &&
((param2.Length > 0)? user.Param2 == param2 : 1 == 1) &&
((param3 != null)? user.Param3 == param3 : 1 == 1);
User foundUser = db.Users.SingleOrDefault(checkUser);
}
I had a similar requirement recently and eventually found this in he MSDN.
CSharp Samples for Visual Studio 2008
The classes included in the DynamicQuery sample of the download allow you to create dynamic queries at runtime in the following format:
var query =
db.Customers.
Where("City = #0 and Orders.Count >= #1", "London", 10).
OrderBy("CompanyName").
Select("new(CompanyName as Name, Phone)");
Using this you can build a query string dynamically at runtime and pass it into the Where() method:
string dynamicQueryString = "City = \"London\" and Order.Count >= 10";
var q = from c in db.Customers.Where(queryString, null)
orderby c.CompanyName
select c;
You can create and use this extension method
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool isToExecute, Expression<Func<TSource, bool>> predicate)
{
return isToExecute ? source.Where(predicate) : source;
}
Just use C#'s && operator:
var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical")
Edit: Ah, need to read more carefully. You wanted to know how to conditionally add additional clauses. In that case, I have no idea. :) What I'd probably do is just prepare several queries, and execute the right one, depending on what I ended up needing.
You could use an external method:
var results =
from rec in GetSomeRecs()
where ConditionalCheck(rec)
select rec;
...
bool ConditionalCheck( typeofRec input ) {
...
}
This would work, but can't be broken down into expression trees, which means Linq to SQL would run the check code against every record.
Alternatively:
var results =
from rec in GetSomeRecs()
where
(!filterBySeverity || rec.Severity == severity) &&
(!filterByUser|| rec.User == user)
select rec;
That might work in expression trees, meaning Linq to SQL would be optimised.
Well, what I thought was you could put the filter conditions into a generic list of Predicates:
var list = new List<string> { "me", "you", "meyou", "mow" };
var predicates = new List<Predicate<string>>();
predicates.Add(i => i.Contains("me"));
predicates.Add(i => i.EndsWith("w"));
var results = new List<string>();
foreach (var p in predicates)
results.AddRange(from i in list where p.Invoke(i) select i);
That results in a list containing "me", "meyou", and "mow".
You could optimize that by doing the foreach with the predicates in a totally different function that ORs all the predicates.

Categories