I have a course table which I need to search based on keywords typed in the search box.
Here is a sample query:
SELECT * FROM Courses WHERE
Title LIKE '%word%' OR Title LIKE '%excel%' OR
Contents LIKE '%word%' OR Contents LIKE '%excel%'
How can I convert this in LINQ where LINQ would dynamically generate WHERE statements based on each keywords.
I tried to user PredicateBuilder it works fine as long as the field is VARCHAR. For the "TEXT" fields the quotes are not generated thus causing compiler to give an error message. Here is the SQL generated by PredicateBuilder
SELECT [t0].[CoursesID], [t0].[Title], [t0].[Contents], [t0].[Active],
FROM [dbo].[Courses] AS [t0]
WHERE ([t0].[Title] LIKE '%word%') OR ([t0].[Contents] LIKE %word%) OR
([t0].Title] LIKE '%excel%') OR ([t0].[Contents] LIKE %excel%)
Notice there is no single Quote for the "Contents" field which is a Text field in the database.
Is there any easy way to build WHERE statement and attach it with query? Does anyone know how I can do this without PredicateBuilder?
Thanks in advance.
Since you are working w/ LINQ I suppose you are working against a LINQ-to-SQL data context right? I don't have a spare DataContext lying around to test this, but this should give you some ideas.
I don't know if it will work against data context though, but most of these are pretty basic stuff (chaining OR operator and Contains method call) so it shouldn't cause problem when the query translates to SQL.
First I create a custom function that would build my predicate:
Func<string, Func<DataItem, bool>> buildKeywordPredicate =
keyword =>
x => x.Title.Contains(keyword)
|| x.Contents.Contains(keyword);
This is a function which takes a single string keyword and then return another function which takes a DataItem and checks it against the keyword.
Basically, if you pass in "Stack", you'll get a predicate: x => x.Title.Contains("Stack") || x.Contents.Contains("Stack").
Next, since there are many possible keywords and you need to chain it with an OR operation, I create another helper function to chain 2 predicates together with an OR
Func<Func<DataItem,bool>, Func<DataItem, bool>, Func<DataItem, bool>> buildOrPredicate =
(pred1, pred2) =>
x => pred1(x) || pred2(x);
This function takes 2 predicates and then join them up with an OR operation.
Having those 2 functions, I can then build my where predicate like this:
foreach (var word in keywords) {
filter = filter == null
? buildKeywordPredicate(word)
: buildOrPredicate(filter, buildKeywordPredicate(word));
}
The first line inside the loop basically checks if the filter is null. If it is, then we want a simple keyword filter built for us.
Else if the filter is not null, we need to chain existing filters with an OR operation, so we pass the existing filter and a new keyword filter to buildOrPredicate to do just that.
And then we can now create the WHERE part of the query:
var result = data.Where(filter);
Passing in the complicated predicate we've just built.
I don't know if this will different from using PredicateBuilder but since we are deferring query translation to the LINQ-to-SQL engine, there should not be any problems.
But as I said, I havn't tested it against a real data context, so if there's any problems you can write in the comments.
Here's the console app that I built to test: http://pastebin.com/feb8cc1e
Hope this helps!
EDIT: For a more generic and reusable version which involves properly utilizing the Expression Trees in LINQ, check out Thomas Petricek's blog post: http://tomasp.net/articles/dynamic-linq-queries.aspx
As predicate builder doesn't know the DB type of the property the Contains method is called on, I guess this might be a problem inside linq to sql. Have you tried with a normal query (not with predicate builder) and a TEXT column with Contains?
Related
I am trying to do string replace on entries of a column inside a db table. So far, I have reached till here:
$misa = DB::table('mis')->pluck('name');
for($i=0;;$i++)
{
$misa[$i] = substr_replace("$misa[$i]","",-3);
}
The error I am getting is "Undefined offset:443".
P.S. I am not a full-fledged programmer. Only trying to develop a few simple programs for my business. Thank You.
Since it's a collection, use the transform() collection method transform it and avoid this kind of errors. Also, you can just use str_before() method to transform each string:
$misa = DB::table('mis')->pluck('name');
$misa->transform(function($i) {
return str_before($i, ':ut');
});
There are a few ways to make this query prettier and FASTER! The beauty of Laravel is that we have the use of both Eloquent for pretty queries and then Collections to manage the data in a user friendly way. So, first lets clean up the query. You can instead use a DB::Raw select and do all of the string replacing in the query itself like so:
$misa = DB::table('mis')->select(DB::raw("REPLACE(name, ':ut' , '') as name"));
Now, we have a collection containing only the name column, and you've removed ':ut' in your specific case and simply replaced it with an empty string all within the MySQL query itself.
Surprise! That's it. No further php manipulation is required making this process much faster (will be noticeable in large data sets - trust me).
Cheers!
I have an EntityCollection ec in C# which has been populated with all Accounts.
Now I want another List or EntityCollection from ec which has all the accounts with status active.
I am using Linq for the same.
But both form of LINQ returns a an empty result while ec has 354 number of records
var activeCRMEC = (from cl in ec.Entities
where cl.Attributes["statecode"].ToString()=="0"
select cl);
OR
var activeCRMEC = ec.Entities.Where(x => x.Attributes["statecode"].ToString() == "0");
Each time the resultset is empty and I am unable to iterate over it. And 300 or so accounts are active, I have checked.
Same thing happens when I use some other attribute such as name etc.
Please be kind enough to point out my mistake.
You can Generate Early Bound Classes to write Linq Queries.
or Else
You can Write Linq Queries Using Late Bound Using OrganizationServiceContext Class.
For Your Reference:
OrganizationServiceContext OrgServiceCOntext = new OrganizationServiceContext(service);
var RetrieveAll = OrgServiceCOntext.CreateQuery("account").
ToList().Where(w => (w.GetAttributeValue<OptionSetValue>("statecode").Value ==0)).Select(s=>s);
I'll give you a few hints, and then tell you what I'm guessing your issue is.
First, use early bound entities. If you've never generated them before, use the earlybound generator, you'll save yourself a lot headaches.
Second, if you can't use early bound entities, use the GetAttribute() method on the Entity class. It'll convert types for you, and handle null reference issues.
Your LINQ expressions look to be correct, so either the ec.Entities doesn't have any entities in it that match the criteria of "statecode" equaling 0, or you possibly have some differed execution occurring on your IEnumerables. Try calling ToList() on the activeCRMEC immediately after the LINQ statement to ensure that is not your issue.
The statecode is an OptionSetValue, you should cast it in this way
((OptionSetValue)cl.Attributes["statecode"]).Value == 0
or
cl.GetAttributeValue<OptionSetValue>("statecode").Value == 0
Both ways are valid and you should ask for the Value that it is an int.
Hope this can help you.
I have an OData URI that works as I want, passing a value for a parameter called gridsize and retrieving the data from Results. This is the URI and it works fine:
http://<webservice>/MULTI_POINT_PARAMParameters(gridsize=0.1m)/Results
I am trying to get the above URI to work using LINQ. I am using an MVC service reference to generate the proxy class. So I tried this LINQ:
var query = (from x in context.MULTI_POINT_PARAMParameters
where
x.gridsize == 0.1M
select x);
However the above LINQ generates this URI, which fails saying "segement not found":
http://<webservice>/MULTI_POINT_PARAMParameters()?$filter=gridsize eq 0.1M}
What I really want LINQ to generate is this, which I know works:
http://<webservice>/MULTI_POINT_PARAMParameters(gridsize=0.1m)/Results
How can I get LINQ to generate the URI I want? I've looked at Linq2rest but could not see how it can help me if I want to explicitly code the LINQ terms myself, rather than have Linq2rest generate "hidden" terms I cannot see.
As far as my knowledge if you want to add filter in OData you have to use "$filter".
if you want to use "MULTI_POINT_PARAMParameters(gridsize=0.1m)" then you might have to use dynamic generation of LINQ.
After dynamic generation your query might look something like this
var query = (from x in context.MULTI_POINT_PARAMParameters(gridsize=0.1m)
select x);
I am just trying to give you a direction to think.Lets see what others have opinion on your question.
I am executing a raven query in C#, and utilising both the Where() and Search() extension methods.
I need both these functionalities, because I need to only return indices with a specific Guid field, AND text that exists in a body of text.
Unfortunatly, the Where extension method seems to not be compatible with the Search extension method. When I combine them I get a Lucene query like this:
Query: FeedOwner:25eb541c\-b04a\-4f08\-b468\-65714f259ac2 MessageBody:<<request*>>
Which seems to completely ignore the 'MessageBody' part of the criteria - so it doesnt matter what constraint I use in the 'free text', it doesnt use it.
I have tested with the 'Search' alone, and it works - so its not a problem with free-text searching by itself - just combining the two.
Thanks to #Tobias on Raven#GoogleGroups who pointed me in the right direction - there was an option to define how the Where and Search clauses would be combined:
Query<T>.Search(candidate => candidate.MessageBody, queryString + "*", options: SearchOptions.And);
Most of the examples I've found deal with Linq to entities, which is not what I need. I have a standard DataTable which I need to modify before returning to the caller. I can iterate over the normal Table.Rows collection or do something like this with the new extension methods:
foreach (var x in table.AsEnumerable()) {
if (x.Field<int>("SomeField") > SomeValue)
x.SetField<string>("OtherField", "OtherValue");
}
But I'm still manually looping through the entire row collection. Not necessarily a big deal, but I'm wondering if there's a more elegant way to accomplish this with Linq somehow, in the sense that I need to create an expression that iterates over the results of a query and performs an arbitrary action, rather than just select elements from the container being enumerated.
I think what you're wondering is if there's some sort of extension method like
stuff.ForEach(x => x.Value = "new value");
Unfortunately, there is no such thing. When I began using LINQ to SQL, I wanted the same thing. But unfortunately, you must use a for loop.