I'd like to build some linq or alternatively, build a query string on the fly and pass it to a WCF Data Service (with Entity Framework data model).
Something like this:
public List<DocumentInformationRecord> SearchClientDocs(string clientCode,
string clientName, string contactName, string groupCode, string groupName,
string filename, string createdby, DateTime dateFrom, DateTime dateTo)
{
List<DocumentInformationRecord> results = new List<DocumentInformationRecord>();
if(!string.IsNullOrEmpty(clientCode))
//Add the client code clause...
etc..
var qry = from c in context.DocumentInformationRecord.where(dynamicQuery);
//Etc......
Any ideas? I tried the predicate builder (http://www.albahari.com/nutshell/predicatebuilder.aspx) but got some invalid operation exceptions.....
I am not sure I entirely understand your question, but I have sometimes written code to build up LINQ queries with different parts depending on input. Typically that goes something like this:
var qry = from item in someList
select item;
if (nameFilter != null)
{
qry = qry.Where(item => item.Name == nameFilter);
}
if (someOtherFilter != null)
{
qry = qry.Where(item => item.SomeOtherStuff == someOtherFilter);
}
// and so on
That way you can build the query step by step. You can do this because of deferred execution; the query will not be executed against the data source until you start iterating over the result.
Not sure if it will suit your situation, but you can use expression trees to build dynamic queries. There is a good tutorial here
Related
I am facing a problem in searching a exact record by LINQ query method in ASP.NET Web API my controller. This is my code:
[HttpGet]
[Route("api/tblProducts/AllProductbySearch/{SearchText}")]
[ResponseType(typeof(IEnumerable<tblProduct>))]
public IHttpActionResult AllProductbySearch(string SearchText)
{
IEnumerable<tblProduct> tblProduct = db.tblProducts.Where(x=>x.PrdKeyword.Contains(SearchText)).AsEnumerable();
if (tblProduct == null)
{
return NotFound();
}
return Ok(tblProduct);
}
In this I am searching the record with value have keyword column and getting the result but problem is that it is not giving exact result for example if in database two record have keyword column value like shirt and another have Tshirt
Then if I pass shirt in SearchText or pass tshirt in SearchText it is giving both record while I want one record which exact match with SearchText. Please help me
My updated action method code is:
[HttpGet]
[Route("api/tblProducts/AllProductbySearch/{SearchText}")]
[ResponseType(typeof(IEnumerable<tblProduct>))]
public IHttpActionResult AllProductbySearch(string SearchText)
{
IEnumerable<tblProduct> tblProduct = db.tblProducts.Where(x => CheckWord(x.PrdKeyword, SearchText)).AsEnumerable();
if (tblProduct == null)
{
return NotFound();
}
return Ok(tblProduct);
}
private bool CheckWord(string source, string searchWord)
{
var punctuation = source.Where(Char.IsPunctuation).Distinct().ToArray();
var words = source.Split().Select(x => x.Trim(punctuation));
return words.Contains(searchWord, StringComparer.OrdinalIgnoreCase);
}
But is throwing the same error - http 500
EDITED 2
Added ToList() - db.tblProducts.ToList().... In this case we retrieve all data from Data Base and filter them in memory. If we don't retrieve all data before filtering .Net tries to create request to SQL with filtration and can't because there are .Net methods as CheckWord().
I think we can get required data without retrieving all table into memory, but don't know how. As variant we should write specific Stored Procedure and use it. Get all into memory is a simplest way (but not faster)
Please, look at this post Get only Whole Words from a .Contains() statement
Actually, for your case solution can be:
IEnumerable<tblProduct> tblProduct = db.tblProducts.ToList()
.Where(x => Regex.Match(x.PrdKeyword, $#"\b{SearchText}\b", RegexOptions.IgnoreCase).Success)
.AsEnumerable();
Option 2. Without regexp:
public static bool CheckWord(string source, string searchWord)
{
if (source == null)
return false;
var punctuation = source.Where(Char.IsPunctuation).Distinct().ToArray();
var words = source.Split().Select(x => x.Trim(punctuation));
return words.Contains(searchWord, StringComparer.OrdinalIgnoreCase);
}
[HttpGet]
[Route("api/tblProducts/AllProductbySearch/{SearchText}")]
[ResponseType(typeof(IEnumerable<tblProduct>))]
public IHttpActionResult AllProductbySearch(string SearchText)
{
IEnumerable<tblProduct> tblProduct = db.tblProducts.ToList()
.Where(x => CheckWord(x.PrdKeyword, SearchText)).AsEnumerable();
if (tblProduct == null)
{
return NotFound();
}
return Ok(tblProduct);
}
Sorry, I'm from phone now, there can be mistakes here. Will try it in 3-4 hour
You are making a simple mistake. You just need to use .Equals instead of .Contains.
When you use Contains .Net will check if the input string is part of the main string. Whereas Equals will check for exact match.
var mainStr = “long string with Hello World”;
var inputStr = “Hello”;
var status = mainStr.Contains(inputStr);
// Value of status is `true`
status = mainStr.Equals(inputStr);
// Value of status is `false`
So your code should look like this:
IEnumerable<tblProduct> tblProduct = db.tblProducts.Where(x=>x.PrdKeyword.Equals(SearchText)).AsEnumerable();
.Equals can also help you find exact match with or without having case-sensitive check in force. The single-parameterised method does a Case-Sensitive check whereas the other overridden methods of .Equals gives you an opportunity to ignore it.
Hope this helps!
How to use string extension method in linq query:
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.FirstOrDefault(x => x.Name.ToFriendlyUrl() ==
name.ToFriendlyUrl());
return newsType;
}
Above query x.Name.ToFriendlyUrl() is not allowed at the minute. Is anyone know how to achieve with it.
Extension methods are indeed allowed in LINQ queries, moreover the LINQ methods themselves are implemented as extension methods.
It's quite another issue however, to use extension methods (or most other methods) in LINQ-to-SQL or LINQ-to-Entities queries. Those queries are not actually run in the C# code, but they are treated like expressions that are translated to SQL. I.e.
db.News.Where(x => x.Published).Select(x => x.Name)
is translated to the SQL Statement
Select Name
From News
Where Published = 1
and it's results are returned to the C# code.
Since there is not way to transfer the ToFriendlyUrl() method to SQL, your code throws an error.
You have basically, two solutions/workarounds. One is to transform the call to a form could be translated into SQL, e.g. if the ToFriendlyUrl() method was just:
public static string ToFriendlyURL(this string value)
{
return value.ToLower();
}
you can inline that code in the LINQ call, and that would work. If however, the methods is more complex, than your only solution is to just fetch the data from the base and then process it on the C# side:
var newsTypeQuery = db.NewsTypes.Where(x => // other conditions, if any);
var newsTypes = newsTypes.ToList(); //forces execution of the query
// the result is now a C# list
var newsType = newsTypes.FirstOrDefault(x =>
x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
Assuming the NewsTypes is an IQueryable this is a result of Entity Framework not being able to convert you extension method into SQL (how should it?). Unless you can rewrite your predicate into something that Entity Framework can translate into SQL you will have to perform the query client side:
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.AsEnumerable().FirstOrDefault(x => x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
return newsType;
}
Notice how AsEnumerable() has been added before FirstOrDefault. Unfortunately this may pull all the rows returned by NewsTypes from the server to client and thus may be quite costly.
This
var newsType = db.NewsTypes.FirstOrDefault(
x => x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
can't be done in Entity Framework. ToFriendlyUrl is an extension method. It's something that is in the "client" computer. The query will be executed on the SQL server. The SQL server doesn't have a ToFriendlyUrl function.
The "standard" solution is to save in a second column named FriendlyName a precalculated version of the ToFriendlyUrl(), so your query becomes:
var friendlyName = name.ToFriendlyUrl();
var newsType = db.NewsTypes.FirstOrDefault(
x => x.FriendlyName == friendlyName);
Instead try like this
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.FirstOrDefault(x => x.Name == name).ToFriendlyUrl();
return newsType;
}
I am trying to send a Linq query as a string to a method to be used in a where clause. Since IEnumerable wouldn't work for this, I have converted my IEnumerable to IQueryable and still it throws error. The following is the code:
public static void FilterData(string Query)
{
if((List<MemberMaintenanceData>)HttpContext.Current.Session["Allmembers"] != null)
{
//Get the IEnumerable object colection from session
var data = (List<MemberMaintenanceData>) HttpContext.Current.Session["Allmembers"];
//Convert it to IQueryable
IQueryable<MemberMaintenanceData> queryData = data.AsQueryable();
//This line doesn't compile!!
queryData = queryData.Where(Query);
HttpContext.Current.Session["Allmembers"] = queryData.AsEnumerable().ToList();
}
}
I intended passing "a => a.AccountId == 1000" as Query
There is a free (and open source) library, provided by Microsoft for parsing strings into Lambda expressions that can then be used in Linq queries. It also contains versions of the standard query operators such as Where() that take a string parameter. You can find it described in Scott Guthries blog post on Dynamic Linq.
For example, you can do queries like this (adapted from a snippet from the Scott guthrie link)
// imagine these have come from a drop down box or some other user input...
string thingToSelectBy = "City";
string citySelectedByUser = "London";
int minNumberOfOrders = 10;
string whereClause = String.Format("{0} = #0 and Orders.Count >= #1", thingToSelectBy);
var query = db.Customers
.Where(whereClause, citySelectedByUser, minNumberOfOrders)
.OrderBy("CompanyName")
.Select("new(CompanyName as Name, Phone");
The Where clause in thisw code snippet shows how you create a where clause using a parameterised string and then dynamically inject values for the parameters at run time, for example, based on user input. This works for parameters of any type.
In your example, the where clause would be
whereClause = "AccountId = 1000";
So in effect you would be doing something like
var newFilteredQueryData = queryData.Where("AccountId = 1000");
That link also contains the location where you can download the source code and a comprehensive document describing the dynamic query API and expression language.
Given a class such as:
public class foo
{
public int AccountID {get;set;}
}
You should be able to do something like this:
Expression<Func<foo, bool>> filter = f => f.AccountID == 1000;
And then pass that as your query. If it is really needed as a string you can do this:
filter.ToString();
//By Using this library
using System.Linq.Dynamic.Core;
InventoryList = Repository.GetAll(); // IQueryable
string filterString = "UnitPrice > 10 And Qty>100 OR Description.Contains("Dairy")";
var filteredGenericList = InventoryList.Where(filterString);
Is it possible ...??? I have 4 DropDownLists on my main page and the
user may select from any, all or some of
the DropDownLists. I am capturing their selection (or non-selection) using a SESSION
variable. What I would like to be able to do is pass the session
variable values to my Data Access Layer and build a WHERE clause
(maybe using StringBuilder) and then place that variable SOMEHOW into
my query expression. Is that possible??? Sorry, I'm a newbie. Thanks ~susan~
public class DLgetRestaurants
{
FVTCEntities db = new FVTCEntities();
public List<RESTAURANT> getRestaurants(string cuisineName, string priceName, string cityName)
[Build a string based on the values passed to the function]
{
var cuisineID = db.CUISINEs.First(s => s.CUISINE_NAME == cuisineName).CUISINE_ID;
List<RESTAURANT> result = (from RESTAURANT in db.RESTAURANTs.Include("CITY").Include("CUISINE").Include("Price")
where **[USE STRINGBUIDER EXPRSSION HERE]**
select RESTAURANT).ToList();
return result;
}
}
You can compose Where conditions which are linked by a logical AND relatively easy in LINQ extension method syntax:
var query = db.RESTAURANTs.Include("CITY").Include("CUISINE").Include("Price");
if (userHasSelectedInDDL1)
query = query.Where(r => r.PropertyForDDL1 == ValueFromDDL1);
if (userHasSelectedInDDL2)
query = query.Where(r => r.PropertyForDDL2 == ValueFromDDL2);
if (userHasSelectedInDDL3)
query = query.Where(r => r.PropertyForDDL3 == ValueFromDDL3);
if (userHasSelectedInDDL4)
query = query.Where(r => r.PropertyForDDL4 == ValueFromDDL4);
List<RESTAURANT> result = query.ToList();
For a much more flexible solution to build queries dynamically the Dynamic LINQ Library recommended by boca is probably the better choice.
I have done this in the past using the Dynamic Linq Library.
I have IQueryable object and I need to take the data inside the IQueryable to put it into Textboxs controls. Is this possible?
I try something like:
public void setdata (IQueryable mydata)
{
textbox1.text = mydata.????
}
Update:
I'm doing this:
public IQueryable getData(String tableName, Hashtable myparams)
{
decimal id = 0;
if (myparams.ContainsKey("id") == true)
id = (decimal)myparams["id"];
Type myType= Type.GetType("ORM_Linq." + tableName + ", ORM_Linq");
return this.GetTable(tableName , "select * from Articu where id_tipo_p = '" + id + "'");
}
public IQueryable<T> GetTable<T>(System.Linq.Expressions.Expression<Func<T, bool>> predicate) where T : class
{
return _datacontext.GetTable<T>().Where(predicate);
}
This returns a {System.Data.Linq.SqlClient.SqlProvider+OneTimeEnumerable1[ORM_Linq.Articu]}`
I don't see any method like you tell me. I see Cast<>, Expression, ToString...
EDIT: Updated based on additional info from your other posts...
Your getData method is returning IQueryable instead of a strongly typed result, which is why you end up casting it. Try changing it to:
public IQueryable<ORM_Linq.Articu> getData(...)
Are you trying to query for "Articu" from different tables?
With the above change in place, your code can be rewritten as follows:
ORM_Linq.Articu result = mydata.SingleOrDefault();
if (result != null)
{
TextBoxCode.Text = result.id.ToString();
TextBoxName.Text = result.descrip;
}
If you have a single result use SingleOrDefault which will return a default value if no results are returned:
var result = mydata.SingleOrDefault();
if (result != null)
{
textbox1.text = result.ProductName; // use the column name
}
else
{
// do something
}
If you have multiple results then loop over them:
foreach (var item in mydata)
{
string name = item.ProductName;
int id = item.ProductId;
// etc..
}
First, you should be using a strongly-typed version of IQueryable. Say that your objects are of type MyObject and that MyObject has a property called Name of type string. Then, first change the parameter mydata to be of type IQueryable<MyObject>:
public void setdata (IQueryable<MyObject> mydata)
Then we can write a body like so to actually get some data out of. Let's say that we just want the first result from the query:
public void setdata (IQueryable<MyObject> mydata) {
MyObject first = mydata.FirstOrDefault();
if(first != null) {
textbox1.Text = first.Name;
}
}
Or, if you want to concatenate all the names:
public void setdata(IQueryable<MyObject> mydata) {
string text = String.Join(", ", mydata.Select(x => x.Name).ToArray());
textbo1.Text = text;
}
Well, as the name suggests, an object implementing IQueryable is... Queryable! You'll need to write a linq query to get at the internal details of your IQueryable object. In your linq query you'll be able to pull out its data and assign bits of it where ever you'd like - like your text box.
Here's a great starting place for learning Linq.
I think you find the same mental struggle when coming from FoxPro and from DataSet. Really nice, powerful string-based capabilities(sql for query, access to tables and columns name) in these worlds are not available, but replaced with a compiled, strongly-typed set of capabilities.
This is very nice if you are statically defining the UI for search and results display against a data source known at compile time. Not so nice if you are trying to build a system which attaches to existing data sources known only at runtime and defined by configuration data.
If you expect only one value just call FirstOrDefault() method.
public void setdata (IQueryable mydata)
{
textbox1.text = mydata.FirstOrDefault().PropertyName;
}