How Would I Write This In LINQ2SQL? - c#

I am slowly porting over an app from MySQL to use Linq2Sql - but one query has stumped me a bit.
SELECT * FROM Pages WHERE DomainID = #reportid AND (PageContent REGEXP 'display:[ \t]*none') > 0 ORDER BY URL ASC
Any ideas on how I would write something like this with Linq2SQL? Its the REGEXP bit thats got me stumped?

There is no way built in to LINQ to SQL, but you have a couple of other choices. The first is to load your strings in as in-memory objects which you can apply Regex functions to. I'm not a big fan of this since it looks like you're potentially getting some very big strings to match against.
The second option is to leverage SQL CLR as described here. This effectively lets you create a stored procedure that gets linked to a CLR method that you create. Whenever you call the method in a LINQ to SQL context, it gets converted to a stored procedure call. Then you use a query like this:
var q = from p in context.Pages
where p.DomainId == reportId &&
RegExMatch(p.PageContent, "display\:[ \t]*none")
select p;

Why not use LINQ to return items that match on reportid and that contain 'display:', to minimise the amount of data being returned from the server, and then use regex on client side to filter that list down?
var query = Pages.Where( p => p.DomainId == 1 && p.PageContent.IndexOf("display:") > 0).OrderBy( o => o.URL );
var regex = new Regex(#"display\:[\t]*none");
foreach (var page in query)
{
if( regex.IsMatch(page.PageContent) )
{
// Do whatever...
}
}

Related

How can I issue a LINQ query to a SQL Server to check if a column value starts with a word?

I have this already
if (options.English != null) query = query
.Where(w => w.English.Contains(options.English));
What I would like to do is to extend this (maybe with another if clause) to make it so that if:
a user enters ^abc then my query would check if the word starts with "abc".
a user abc then it would check if the column contains "abc"
I am using a SQL Server back-end database. Could anyone give me a suggestion as to how I could implement this functionality.
Assuming that you're using Entity Framework, you can use the StartsWith() and EndsWith() methods, to achieve the same results as Contains() except only at the beginning or the end of a string. It will generate the code for you.
Then simply create conditional statements in your code, in order to determine which one of the methods you should use.
Some word of advice:
There might be a bug with EF Core, in which it turns StartsWith("string") into LIKE "string%" which might yield incorrect results with strings, containing wildcard characters such as "_".
So I'd advise you to use plain SQL with EF Core, and given that you're using SQL Server as a DBMS, query like that:
if (searchText.StartsWith("^"))
{
var result = query.FromSql($"SELECT something FROM table WHERE PATINDEX({searchText.Substring(1)}, something) = 1");
}
else
{
var result = query.FromSql($"SELECT * FROM table WHERE PATINDEX({searchText.Substring(1)}, something ) <> 0");
}
With PATINDEX() you will get correct results even if your pattern string contains wildcard characters - escaping potential bugs with relying on StartsWith() and EndsWith() to generate proper SQL code.
But that's only for EF Core, EF 6 works like a charm the way other people answered :)
You can put that choice in a conditional statement:
IQueryable<Whatever> query = ...;
if (searchText.StartsWith("^"))
{
query = query.Where(w => w.English.StartsWith(searchText.Substring(1)));
}
else
{
query = query.Where(w => w.English.Contains(searchText));
}
You can also do the same comparison inline, but that'll generate very ugly SQL, if it even works:
query = query.Where(w =>
searchText.StartsWith("^")
? w.English.StartsWith(searchText.Substring(1))
: w.English.Contains(searchText));
Do note that you generally don't want to search text using SQL, as that results in a pretty poor user experience. Take a look at full-text indexing.
if (options.English != null)
{
bool englishStartsWith = options.English.StartsWith("^");
if(englishStartsWith)
{
query = query.Where(w => w.English.StartsWith(options.English.Substring(1)));
}
else
{
query = query.Where(w => w.English.Contains(options.English));
}
}

Dynamic Linq - Joining a table with 1 to many relationship

I'm using Dynamic Linq as the backend to an in-app reporting tool and I've hit a problem that I can't get round where I have to access a table that has a 1:M relationship .
My simplified data structure is this:
If I were querying this in standard Linq I'd write the query as:
from a in context.Table_A
select new
{
a.RefNo,
val = from b in a.Table_B
where (b.A_ID == a.ID)
where (b.code == "A0001"
select(b.Value).FirstOrDefault()
}
This works without any problem. However, when I try the query using Dynamic Linq I can't get the join to work.
From the code below you can see what I'm getting at but obviously I can't use the "a." and the "a.Table_B" references in the query. What do I have to do to be able to access Table_B in this context?
string select = "new (Ref_No,
val = from b in a.Table_B
where (b.A_ID == a.ID)
where (b.code == \"A0001\"
select(b.Value).FirstOrDefault()";
var results = context.Table_A.Select(select);
Edit 1:
To answer #Hogan's comment - Why don't I use join: The reports system is dynamic and the select statement may or may not be joining on to Table_B (or indeed joining on to Table_B multiple times) so the join has to be optional. My other issue with this is that unlike the Select method where I can pass in a string as a parameter (allowing me to make it dynamic quite easily) the Join() method can't be called in that way. The closest thing I've found is a dynamic Linq join extention method, something I may have to consider using but I've a feeling that this will be cumbersome with the dynamic select().
Edit 2:
Based on Hogan's suggestions I've got this far:
delegate string searchTableA(Table_A a);
public void Search()
{
....
searchTableA sel = (a) =>
{
return (from b in context.Table_B
where (b.A_ID == a.ID)
select (b.Value)).FirstOrDefault();
};
var res = context.Table_A.Select(sel);
}
This gives the error: 'System.Data.Entity.DbSet<TestDynamicLinqJoins.Table_A>' does not contain a definition for 'Select' and the best extension method overload 'System.Linq.Dynamic.DynamicQueryable.Select(System.Linq.IQueryable, string, params object[])' has some invalid arguments
Hard to give exact code because I don't know the types of your elements, but something like this would work fine using delegates.
delegate string searchTableA(elementType a);
searchTableA sel = (a) =>
{
return from b in a.Table_B
where (b.A_ID == a.ID)
where (b.code == "A0001")
select(b.Value).FirstOrDefault();
};
var results = context.Table_A.Select(sel);

C# get data from database

I need to enhance a function of C# code. I am new to C# but I have strong java background.
My job is to write a new query, like JDBC, to get data from database.
In the method below, I didn't see any SQL query.
what does this line mean ? is this similar to Hibernate hql ?
from p in Session.Query<MyObject>() select p
Thanks
code:
public IPagingList<MyObject> ReadMyObjectItems(int start, int limit, IList<Filter> filters)
{
var criteria = Session.CreateCriteria<MyObject>();
if (limit != -1)
{
criteria.SetMaxResults(limit);
criteria.SetFirstResult(start);
}
if (filters != null)
{
foreach (var filter in filters)
{
criteria.Add(Restrictions.InsensitiveLike(filter.Field, "%" + filter.Value + "%"));
}
}
IList<MyObject> report = criteria.List<MyObject>();
int total = (from p in Session.Query<MyObject>() select p).Count();
var pagedResults = new PagingList<MyObject> { List = report, Total = total };
return pagedResults;
}
(from p in Session.Query<MyObject>() select p).Count();
We are simply getting a count of all the objects.
Refactor this to
Session.Query<MyObject>().Count(). It's easier to read and in this case LINQ is unnecessary.
It's Linq syntax. A powerful form of querying incorporated into .Net and different .Net frameworks.
In your case it seems you are using NHibernate and that line is using NHibernate Linq. (But the surrounding code is using Criteria.)
For composing queries in NHibernate you have the options of using
Linq
HQL
Criteria
which all have different pros and cons.
Either will be translated into SQL by NHibernate. The actual result will be fetched when you try to access it such as for instance reading an item in the result or converting the result to a List etc.
It seems that project is using an ORM like NHibernate. You can get more details here : http://nhibernate.info

Linq IN statement to exclude from a list

I have two tables
Contracts
Id | StartDate | EndDate
ExcludedContracts
Id | ContractId
I am using the following statements to get both sets of data:
var excludedContracts = from Excluded in
DataContext.ExcludedTransportContracts
select Excluded;
// Get a collection of all live sites first
var liveContracts = from Contracts in DataContext.Contracts
where Contracts.EndDate > DateTime.Now
select Contracts;
I need to select all contracts that don't have a record in the ExcludedContracts table. I've been battling with WHERE queries for a while but had no luck.
How do I do a query similar as I would an instatement IN(1,2,3) SQL?
Thanks!
I don't know which query provider you're using, but if that query provider supports the Contains method you can use this:
//get all contracts that haven't been excluded
var nonExcludedContacts = liveContracts.Where(l => !excludedContracts.Contains(l));
If that method is not supported, you can use the Any method instead. It's not as readable but it gets the job done:
//get all contracts that haven't been excluded
var nonExcludedContacts = liveContracts.Where(l => !excludedContracts.Any(e => e.ContractId == l.Id));
Are the two tables not related at the data level? Ie a Contract object having a list of ExcludedTransportContracts as a property?
From the look of the data this is probably how they should be related, then you can do something like var liveContract = DataContext.Contracts.Where(c => c.ExcludedTransportContracts.Count() == 0 && c.EndDate > DateTime.Now )
Was off the top of my head so the code may need slight alterations.
the equivilent of in in LINQ is contains http://msdn.microsoft.com/en-us/library/system.linq.enumerable.contains.aspx however you should also look at linq except method http://msdn.microsoft.com/en-us/library/system.linq.enumerable.except.aspx
Update: as mentioned those are the IEnumerable ones so should be http://msdn.microsoft.com/en-us/library/bb341422.aspx for contains and http://msdn.microsoft.com/en-us/library/bb348938.aspx for except which are for Queryable
Try something like this. Obviously, if you use the "Contains" method, you should compare similar datatypes/ objects.
The compiler probably won't compile code when using implicit typed variables in a contains function, but I'm not sure about that.
List<Contracts> excludedContracts =
(from Excluded in DataContext.ExcludedTransportContracts
select Excluded).toList();
List<Contracts> liveContracts =
(from Contracts in DataContext.Contracts
where Contracts.EndDate > DateTime.Now &&
!excludedContracts.Contains(Contracts)
select Contracts).toList();
you can use the Enumerable.Contains() function for this task.

Generic parsing of Expressiontree to SQL

So, im having this very simple linq provider that does stuff with objects in a small constrained domain. No need for a full blown Linq2Sql implementation, but some of the stuff i need to do involves sql queries. My thougt was that, if you look away from table and column names, most expression tree => sql must be very (very) generic.
Expression<Func<DateTime, bool>> expr = d => d.DateTime.Month == 1
will always translate to
DATEPART({0}, m) = 1
or
Expression<Func<DateTime?, bool>> expr = d => d.HasValue || d == DateTime.Now
into
{0} IS NOT NULL OR {0} = NOW()
hope you get the point. Is there any generic sql generators like this out there?
hehe, dont we just looove hacking around? So, its not FULLY working yet, i need to figure out how to dynamically inject some mapping information, but so far it looks promising.
I've been able to produce this sql
SELECT t0.[Born], t0.[Yo]
FROM [Heies] AS t0
WHERE ((MONTH(t0.[Born]) = 1) OR (NOT (t0.[Yo] IS NULL OR t0.[Yo] = '') AND (t0.
[Born] = #p0)))
with this code
var provider = new DbEntityProvider(new SqlConnection(), new TSqlLanguage(), new ImplicitMapping(), new QueryPolicy());
var exp = provider.GetTable<Hey>().Where(d => d.Born.Month == 1 || (!String.IsNullOrEmpty(d.Yo) && d.Born == DateTime.Now)).Expression;
var sql = ((QueryProvider)provider).GetQueryText(exp);
using the IQToolkit, so thanks to Damian for reminding me about it again! If anyone has a similar way to produce sql via Linq2Sql i would love to hear from you so i could loose the dependency on IQToolkit.
Update
After a lot of fooling around i have hit the wall at extracting the values for each of the parameters. After a lot of source reading it seems like its hidden far away for the actual execution of the query. Well, guess what, i don't want the query to be executed :/
After looking into Linq2Sql i came up with this code that does roughly the same, so maybe i should just stick with that
var xml = Generate<NewsProperty>();
var mapping = XmlMappingSource.FromUrl(xml);
var ctx = new DataContext(new SqlConnection("..."), mapping);
var query = ctx.GetTable<NewsProperty>().Where(n => n.Date.Year == 2010).OrderBy(n => n.Date).Take(5);
var cmd = ctx.GetCommand(query);
string sql = cmd.CommandText;
foreach (DbParameter param in cmd.Parameters)
{
sql = sql.Replace(param.ParameterName, param.Value.ToString());
}
You should take a look at the IQueryable Toolkit and its related articles by Matt Warren http://blogs.msdn.com/b/mattwar/archive/2008/11/18/linq-links.aspx.

Categories

Resources