c# Fluent SQL Helper - Syntax improvement - c#

I'm writing a little library to help building SQL requests (only doing SELECTs for the moment) but I'm not satisfied with an aspect of the syntax, here's an exemple to explain:
var db = FluentDb.WithConnectionString("SqlCeTest");
var query = db.From("Customers")
.Where(FS.Col("Age") > 18 & FS.Col("Name").StartsWith("L"))
.OrderBy("BirthDate")
.Select("Name", "Age", "BirthDate");
var customers = query.ToList((r) => new
{
Name = r.Get<string>("Name"),
Age = r.Get<int>("Age"),
BirthDate = r.Get<DateTime?>("BirtDate")
});
The part I'd like to improve is the FS.Col("ColumnName"), it's supposed to stand for FluentSql.Column (return a new FluentColumn(columnName)), but I find it a bit long in that context, what I'd really like is to be able to use just Col("ColumnName")...
Do anybody see a trick I could use to achieve that, or another syntax idea?
My ideas:
Extension method on string: Where("Name".Col() == "Jon")
Lambda expression with factory object using indexer: .Where(c => c["Name"] == "Jon")
Anyone see something better/shorter/nicer?
Edit:
my second idea looks good but there's a downside if i use it in another context:
I sometime need to use FluentColumns in Select (or OrderBy, or GroupBy) statements like that:
query.Select(FS.Col("Name").As("Customer"), FS.Col("OrderId").Count().As("OrdersCount"));
I would have to repeat the 'c => ' for each column...

A twist on your second option (which is pretty good) would be to use a dynamic expandoobject in the lambda instead of a string indexer.
http://blogs.msdn.com/b/csharpfaq/archive/2009/10/01/dynamic-in-c-4-0-introducing-the-expandoobject.aspx

Just for information, I decided to go with an indexer syntax on the FluentDb instance:
db["Customer", "AddressId"] mean column AddressId of table Customer,
an alternative syntax is available: db["Customer"]["AddressId"]
So in the end, it's gonna be (I still need to find a trick to make the Column declaration without table nice):
.Where(db["Customer", "Name"] == "Jon")

Related

Linq select where has at least one entry in another table

I have the following structure that I wan't to query using Linq, specifically Linq to Entities (Enitity Framework).
Table1: RouteMeta
Table2: SitePage
Multiple SitePages can link to the same RouteMeta.
I'm querying the Route Meta to select a number of rows. I'm using a generic repository, currently like this:
return r.Find().ToList();
There's nothing special about it - the Find method accepts an optional linq expression, so I could do something like this:
return r.Find(x => x.Status=1).ToList();
However, what I actually want to do is to select rows from RouteMeta where at least one linked row exists in SitePages with a property IsPublished = true.
return r.Find(x => x.SitePages("where y => y.IsPublished = true");
Obviously, the above isn't correct, I'm just trying to explain the scenario better.
Any advice appreciated.
try something like
return r.Find(x=>x.Sitepages.Any(y=>y.Published))?
I'd also suggesting using a profiler if possible to check that this translates properly into SQL. It probably should do but it depends on how your repository works.

Case-insensitive "contains" in Linq

I have a mvc project which I use linq in it.
In my database there is some records, for example "Someth ing","SOmeTH ing","someTh ing","SOMETH ING","someTH ING"
I want to do this:
SELECT * FROM dbo.doc_dt_records WHERE name LIKE '%' + #records.Name + '%'
However if I run this code, list.Count returns 0. What should I do?
records.Name = "someth ing"; //for example
var rec = db.Records.ToList();
var lists = rec.Where(p => p.Name.Contains(records.Name)).ToList();
if (lists.Count > 0)
{
// do sthng
}
Thanks for your helps...
the easy way is to use ToLower() method
var lists = rec.Where(p => p.Name.ToLower().Contains(records.Name.ToLower())).ToList();
a better solution (based on this post: Case insensitive 'Contains(string)')
var lists = rec.Where(p =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf
(p.Name, records.Name, CompareOptions.IgnoreCase) >= 0).ToList();
That is totally not a LINQ issue.
Case sensitiivty on the generated SQL depends on the collation relevant for the table. Which in your case likely is case insensitive.
You would get the same result from any SQL you emit.
use IndexOf and StringComparison.OrdinalIgnoreCase:
p.Name.IndexOf(records.Name, StringComparison.OrdinalIgnoreCase) >= 0;
You can create an extension function like this:
public static bool Contains(this string src, string toCheck, StringComparison comp)
{
return src.IndexOf(toCheck, comp) >= 0;
}
To my understanding, this question does not have an unambiguous answer. The matter is that the best way of doing this depends on details which aren't provided in the question. For instance, what exact ORM do you use and what precise DB server you are connected to. For example, if you use Entity Framework against MS SQL Server, you better do not touch your LINQ expression at all. All you need to do is to set the case-insensitive collation on the database/table/column you compare your string with. That will do the trick much better than any change of your LINQ expression. The matter is that when LINQ is translated to SQL, it better be the straight comparison of the column having case-insensitive collation to your string than anything else. Just because it usually works quicker and it is the natural way to do the trick.
You do not want the final query to be something like:
SELECT *
FROM AspNetUsers U
WHERE UPPER(U.Name) LIKE '%SOMETHING%';
It is much better to come up with something like:
SELECT *
FROM AspNetUsers U
WHERE U.Name LIKE '%SOMETHING%';
But with a case-insensitive collation of [Name] column. The difference is that if you have let's say index containing [Name] column, the second query might use it, the first one would do the full scan of the table anyway.
So if let's say records references to DBSet<T> and the record is just one object of type T. You code would be like this:
var lists = records.Where(p => p.Name.Contains(record.Name)).ToList();
And you do the rest on SQL-server. Or if all you need to know is there any value in the list and do not need these values, it would be even better to do like this:
if (records.Any(p => p.Name.Contains(record.Name)))
{
// do something
}
Generally speaking, if you use any sort of ORM connected to any sort of SQL server, you better do case-insensitivity by setting up appropriate parameters of your server/database/table/column. And only if it is impossible or by far too expensive, you consider other possibilities. Otherwise, you might bang into some unexpected and very unpleasant behaviour. For instance, Entity Framework Core 2.x if it cannot translate your LINQ expression straightway into SQL query, is doing different tricks replacing server-side operations with client-side ones. So you can end up with a solution which fetches all data from the table to the client and filter it there. It might be quite a problem if your table is big enough.
As for the situation when LINQ query is processed locally, there are a lot of ways to do the trick. My favourite one is the next:
var lists = records.Where(p => p.Name
.Contains(record.Name, StringComparison.InvariantCultureIgnoreCase))
.ToList();
try this
var lists = rec.Where(p => String.Equals(p.Name,records.Name,StringComparison.OrdinalIgnoreCase)).ToList();
refer here for documentation

LINQ: Multiple LIKE clauses in a single predicate

I'd like to do a LINQ query that can compare multiple variables to a single string. I've seen LINQ for LIKE queries of array elements, and it's helpful, but not quite. I need the reverse.
What I'd like to do is the following: let's say I have a Company object with both Name and Address. I also have a string keyword. Then, I'd like to find all Companys in a list that have the keyword in either their Name or Address. In SQL it would be...
SELECT * FROM Company
WHERE Name LIKE '%keyword%' OR Address LIKE '%keyword%'
I've been using Entity Framework, and I've tried the following: context.Companies.Where(x => new string[] { x.Name, x.Address }.Contains(keyword), as well as context.Companies.Where(x => new string[] { x.Name, x.Address }.Any(r => r.Contains(keyword)), but neither were successful. The first one gives me an IN clause, and the second one... I don't know what it does, but it doesn't give me what I want.
I'm sorry I don't have a very in-depth understanding of Expressions (yet); I wished I was able to write my own custom Expressions from scratch, but scratch it I can't just yet... Can anybody help me with this?
Any reason for not just using the || operator?
context.Companies.Where(x => x.Name.Contains(keyword) ||
x.Address.Contains(keyword))
I'd expect this to be translated into your original SQL.

LINQ to SQL, condition on foreign entities

In SQL, it'd be done as such:
SELECT * FROM Student
WHERE SchoolId IN
(SELECT id FROM School WHERE Name LIKE '%elementary%')
How do I implement this with LINQ? I've tried the following:
var list = context.Students.Where(x => context.Schools.Where(r => r.Name.Contains("elementary").Select(r => r.Id).Contains(x.SchoolId))
but it's not giving me what I want, unfortunately...
I know it's possible to retrieve all the Ids from the School table first, but I think it'd take a heavy toll on the performance. Preferably I'd like LINQ to SQL to handle everything; I can't do this using vanilla SQL because I need stuff to be dynamic and currently LINQ is the best solution for me.
The code above is all for illustration purposes; what I'm doing is a tad different (but more or less the same). I really do need some help on this; if you need any more information just feel free to ask.
EDIT: My bad, I missed out a field. It works, but the results didn't show up because I was missing that field... So sorry...
Try this:
var result = from st in context.Student
from sc in context.Schools
where sc.Name.Contains("elementary") && sc.SchoolId == st.SchoolId
select st;
I am a bit hazy on the syntax, pardon me. But this should point you to the right direction.
Something like this should work. The first use of Contains is on a string object to see if the string contains the substring "elementary". The second use of Contains is on a list and checks to see if the first result list contains SchoolId.
var sublist = from s in context.Schools
where s.Name.Contains("elementary")
select id;
var list = from s in context.Students
where sublist.Contains(s.SchoolId)
select s;

Creating a nhibernate like query builder

I am trying to create something similar to the nhibernate fluent syntax.
I have a class called Query (where t is my class representation of a sql table) and I want to add a Where expression so that it can generate a sql string internally to be executed. I'm simply doing this for learning purposes so that is why I am not using an actual ORM. I dont need anything complex, just trying to learn the basics on how something like this is built with a Func or Expression.
Any help would be greatly appreciated. Thanks!!
I basically want to call:
var query = new Query<MyDomainClass>().Where(x => x.Id == 1);
and have it create a query that says "SELECT * FROM MyDomainClass WHERE Id = 1";
I'm not sure how to get the where part of the object working.
You need to parse the expression tree, see this link for a quick introduction.

Categories