Customizing SQL with Dapper Extensions - c#

I'm using Dapper Extensions for some of my types and it works really well for most use cases. I've run into a case where I have a many-many relationship, and I want to do something like:-
SELECT id,a,b,c FROM Foo WHERE Foo.id in (SELECT foo_id FROM foo-bar WHERE bar-id=#bar_id)
Obviously Dapper Extensions can handle "SELECT id,a,b,c FROM Foo" but not the latter part. I could do a select to get the list of Foo id's I want, and then pass that to Dapper Extensions but that's less efficient.
The part I can't do with plain Dapper is get the SELECT column list automatically, so what I'd really like is a way to:-
Get the SELECT column list from Dapper Extension's internal mechanisms
Get the basic "SELECT id,a,b,c FROM Foo" from Dapper Extension's internal mechanisms
Hook Dapper Extensions Get code so that I can add a custom WHERE clause
I've looked at the code, but I can't spot how to do any of these things. Can anyone help? I've worked around by using plain Dapper and "SELECT * ..." at the moment, but I'm sure there's a better way.

Here is another option:
You can create a View:
select * from Foo
join FooBar b
on a.foo_id = b.foo_id
Then use predicates to select with any where clause:
using (SqlConnection cn = new SqlConnection(_connectionString))
{
cn.Open();
var predicate = Predicates.Field<Foo>(f => f.foo_id, Operator.Eq, 1);
IEnumerable<Foo> list = cn.GetList<Foo>(predicate);
cn.Close();
}
Generated SQL should look something like:
SELECT
[Foo].[foo_id]
, [Foo].[...]
, [Foo].[...]
, [Foo].[...]
, [Foo].[...]
FROM [ViewName]
WHERE ([ViewName].[foo_id] = #foo_id_0)

I did not know this was not supported back in 2012. So about 1.7K views in two years and not much exposure. But in case someone new to Dapper landed here and wondering if it works, the answer is, it is working. Using the latest version of, as of this writing, Dapper v1.42 from nuget:
var sql = "SELECT id,a,b,c FROM Foo WHERE Foo.id in (
SELECT foo_id FROM foo-bar WHERE bar-id=#bar_id)"
using (var cn = new SqlConnection(the_connection_string)) {
cn.Open();
var returnedObject = cn.Query<dynamic>(sql, new { bar_id = some_value });
}

Related

Created new query to search two tables using access wizard but it doesnt work in program?

I have written a query for an access database using the access query maker. Previously I only needed to query one table and all of my code ran perfectly using an old query, however the new query I have created does not run.This leads me to believe the problem is with the query itself. The query does work in access so I am unsure what the problem is. I believe the problem may be that the query is for SQL and and I am using OleDb but I don't have enough experience with either to recognize the difference.
my query is:
SELECT
Chapters.ChapterName,
ChapterScores.ChapterNo,
ChapterScores.AllTimeScore,
ChapterScores.TotalAttempted,
ChapterScores.Unlocked
FROM Chapters
INNER JOIN ChapterScores
ON (Chapters.ModuleID = ChapterScores.ModuleID)
AND (Chapters.ChapterNo = ChapterScores.ChapterNo)
WHERE (((ChapterScores.ModuleID) = '"+modulename+"')
AND ((Chapters.ModuleID) = [ChapterScores].[ModuleID])
AND ((Chapters.ChapterNo) = [ChapterScores].[ChapterNo])
AND ((ChapterScores.UserID) = '"+LoginForm.UserID+"'));
loginForm.UserID is an int32 and modulename is a string.
Links to see what the two tables look like are below
in the program the code looks like this:
OleDbDataReader Reader2;
var getscore = new OleDbCommand("SELECT Chapters.ChapterName, ChapterScores.ChapterNo, ChapterScores.AllTimeScore, ChapterScores.TotalAttempted, ChapterScores.Unlocked FROM Chapters INNER JOIN ChapterScores ON(Chapters.ModuleID = ChapterScores.ModuleID) AND(Chapters.ChapterNo = ChapterScores.ChapterNo) WHERE(((ChapterScores.ModuleID) = '"+modulename+"') AND((Chapters.ModuleID) =[ChapterScores].[ModuleID]) AND((Chapters.ChapterNo) =[ChapterScores].[ChapterNo]) AND((ChapterScores.UserID) = '"+LoginForm.UserID+"'));", connection);
Reader2 = getscore.ExecuteReader();
while (Reader2.Read())
{
Console.WriteLine(Reader2.GetValue(0));
}

EF Core custom count query

I'm working on a small ASP.NET Core project for tagging images using Entity Framework Core on a Sqlite database, mainly just for learning. There are two tables (and POCOs), Tags and Images, where multiple tags are related to each image. I'm trying to get a count of all Images that have tags associated with them.
In plain SQL I'd write SELECT COUNT(DISTINCT ImageId) FROM Tags to get the count, and in LINQ I came up with _context.Tags.Select(t => t.Image).Distinct().Count(). But that LINQ query appears to cause EF-Core to join the two tables, return all of the rows, and then do the Distinct and Count in code.
I tried to do _context.Tags.FromSql("SELECT COUNT(DISTINCT ImageId) FROM Tags"), but because that query only returns the count the call fails because EF can't map the result to a Tag. I also tried to use _context.Database.FromSql<int>, but wasn't able to find any real documentation on it and there doesn't seem to be IntelliSense for it.
What I have done for now is what's detailed in the "ADO.NET" section of this blog post from Eric Anderson:
int count;
using (var connection = _context.Database.GetDbConnection())
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT COUNT(DISTINCT ImageId) FROM Tags";
string result = command.ExecuteScalar().ToString();
int.TryParse(result, out count);
}
}
But is that the best way to go about getting the count efficiently?
Edit: Here's the query that EF is putting in the Debug output:
SELECT "t"."TagId", "t"."Content", "t"."ImageId", "t.Image"."ImageId", "t.Image"."FileName", "t.Image"."Path", "t.Image"."Url"
FROM "Tags" AS "t"
LEFT JOIN "Images" AS "t.Image" ON "t"."ImageId" = "t.Image"."ImageId"
ORDER BY "t"."ImageId"
As of now, you can't define an ad-hoc result.
Good news is that it's currently on the backlog: https://github.com/aspnet/EntityFramework/issues/1862
In the meantime, here's an extension method that would work:
public static int IntFromSQL(this ApplicationDbContext context, string sql )
{
int count;
using (var connection = context.Database.GetDbConnection())
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = sql;
string result = command.ExecuteScalar().ToString();
int.TryParse(result, out count);
}
}
return count;
}
Usage:
int result = _context.IntFromSQL("SELECT COUNT(DISTINCT ImageId) FROM Tags");
Your original line of code should have done exactly what you wanted. It would also be recommended over inline SQL.
_context.Tags.Select(t => t.Image).Distinct().Count()
Are you sure that this called the database for the two tables and then queried them in memory? If you observed this behaviour while debugging then it's possible that your inspection caused the IQueryable to enumerate which would call the database using a different query than it would have otherwise.
A way to check the actual query, without breaking into the running code, is by using the MyLoggerProvider from the Entity Framework Core documentation.
https://docs.efproject.net/en/latest/miscellaneous/logging.html?highlight=logging
Once the logger is registered in the code then any SQL query ran against the server will be displayed in the console window and/or in the file c:\temp\log.txt.
The following log message was generated when using a Distinct() and a Count() on the database tables of the website example.
SELECT COUNT(*)
FROM (
SELECT DISTINCT [a.Blog].[BlogId], [a.Blog].[Url]
FROM [Posts] AS [a]
INNER JOIN [Blogs] AS [a.Blog] ON [a].[BlogId] = [a.Blog].[BlogId]
) AS [t]Closing connection to database '***' on server 'tcp:**************'.
Finally, since you do not need any of the properties on the t.Image then it seems that you should be using a Where() rather than a Select().

LINQ to SQL: retrieve data and put it in a textBox

I'm kinda new to LINQ, so sorry if my question is dumb.
I need to retrieve some values from a database and put them in textBoxes. Just that. Something like the code below, but using LINQ:
EDIT: actually, I want to retrieve more than one field. Like that:
SqlDataReader dr = new functionThatReturnsDataReader();
if (dr.HasRows) {
dr.Read();
txtId = dr["Id"].ToString();
txtName = dr["Name"].ToString();
}
I've found this solution online:
IDbCommand command = dc.GetCommand(query);
command.Connection = dc.Connection;
command.Connection.Open();
IDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection);
However, it seems like I'm trowing away everything that LINQ stands for if I mix it with a standard executeReader. There's no reason in building a data context and query and them execute them like ADO...
How can I achieve the same without using IDbCommand and IDataReader?
I think you need to create a LINQ to SQL class from your database:
http://msdn.microsoft.com/en-us/library/bb384428.aspx
Then you can treat the database table like an object and query it with linq... without the specifics of the query you are trying to run, I cannot construct the linq query for you, but creating the DBML file is the first step.
One thing to think about when comparing a LINQ (or Entity Framework, or other ORM) solution to an ADO.NET solution is that the ORM's are--generally--strongly typed. This means that you need to apply object-oriented principles to them.
If you're dealing with the context, query, and results all in the same function, you can do this:
using(var context = new YourContext())
{
txtId.Text = (from t in context.YourTable
where t.Conditions
select t.Id).FirstOrDefault();
}
If they're not in the same function (and really, they shouldn't be), then something like this would work:
string FunctionThatReturnsId()
{
using(var context = new YourContext())
{
return (from t in context.YourTable
where t.Conditions
select t.Id).FirstOrDefault();
}
}
...
txtId.Text = FunctionThatReturnsId();
If you are using entity Framework then you dont need any query just write id in text box and hit search button it will show you all records in text box of that id.
EntityFramework_mvcEntities db = new EntityFramework_mvcEntities();
int i =Convert.ToInt32( txtsrch.Text);
Employee p = db.Employees.Find(i);
TextBox1.Text = p.Name;
TextBox2.Text = p.Email;
TextBox4.Text = p.Mobile;
db.SaveChanges();
}
var q = from c in context.GetTable<tbl_user>()
where c.user_ID == lbuserid.Text.ToString()
select new
{
c.Username,
c.firstname
};
foreach (var item in q)
{
lbusername.Text = item.Username;
lbfirstname.Text = item.firstname;
}

Can someone tell me why this SQL query not working

I followed this answer,
How can I supply a List<int> to a SQL parameter?
Please see these questions of mine for understanding scenario,
How can I update Crate IDs of List of Fruits in single SQL query in c#
how can i update SQL table logic
What I am trying and not working
private void relate_fruit_crate(List<string> selectedFruitIDs, int selectedCrateID)
{
string updateStatement = "UPDATE relate_fruit_crate set CrateID = #selectedCrateID where FruitID = #selectedFruitIDs";
using (SqlConnection connection = new SqlConnection(ConnectionString()))
using (SqlCommand cmd = new SqlCommand(updateStatement, connection))
{
connection.Open();
cmd.Parameters.Add(new SqlParameter("#selectedCrateID", selectedCrateID.ToString()));
cmd.Parameters.Add(new SqlParameter("#selectedFruitIDs", String.Join(",",selectedFruitIDs.ToArray())));
cmd.ExecuteNonQuery();
}
}
My code runs without any error,
You need to use the IN keyword in your scenario. The problem is that the SqlCommand.Parameters pattern does not build the query itself, but calls a stored procedure on the database:
exec sp_executesql N'UPDATE relate_fruit_crate set CrateID = #selectedCrateID where FruitID in(''#selectedFruitIDs'')', N'#selectedCrateID nvarchar(1),#selectedFruitIDs nvarchar(5)', #selectedCrateID = N'1', #selectedFruitIDs = N'1,2'
This will not work as the array is escaped.
The workaround would be to either use a normal StringBuilder to create the query. (Warning! SQL Injection) or to call the query for each ID separately.
Maybe there's a way to do this with the SqlCommand.Parameters, but I could not find one.
OLD POST::
string updateStatement = "UPDATE relate_fruit_crate set CrateID IN ('#selectedCrateID') where FruitID = '#selectedFruitIDs'";
[....]
cmd.Parameters.Add(new SqlParameter("#selectedFruitIDs", String.Join("','",selectedFruitIDs.ToArray())));
and equals (=) query will only match a single value.
Multi-value parameter queries are a bit of a pain in TSQL. There are options like table-valued parameters, or "split" UDFs - otherwise... it is a bit tricky. You end up having to add multiple parameters (depending on the data), and change the query to suit. If I may suggest... a library like "dapper" may help you here - it is designed to make scenarios like this easy:
using Dapper; // at the top of your code file, to enable dapper
...
private void relate_fruit_crate(List<string> selectedFruitIDs, int selectedCrateID)
{
// note the slightly unusual "in" here (no paranethesis) - that is because
// dapper is going to do some voodoo...
using (SqlConnection connection = new SqlConnection(ConnectionString()))
{
connection.Open();
connection.Execute(
"UPDATE relate_fruit_crate set CrateID = #selectedCrateID where FruitID in #selectedFruitIDs",
new { selectedFruitIDs, selectedCrateID });
}
}
here "dapper" does all the work of figuring out how to express that in using multiple parameters, adding the correct number of parameters. It is also just much easier (in particular, look at how little work we did with commands and parameters; it handles readers nicely too).
Dapper is freely available from NuGet

How can I create a LINQ-to-SQL statement when I have table name as string?

If I have the name of a database table like this:
string tableName = "Addresses";
string tableName = "Customers";
How can I construct a dynamic LINQ statement like this:
var items = from o in db.{tableName}
select o;
foreach (var item in items)
{
sb.Append(item.Id + Environment.NewLine);
}
I know I could do something like this:
IEnumerable<Customer> results = db.ExecuteQuery<Customer>
("SELECT contactname FROM customers WHERE city = {0}",
"London");
But in this instance I don't want strongly typed objects as my result, I just want a recordset to pick apart.
Answer:
Thanks Shalkalpesh, I took your advice and solved this by just avoiding LINQ altogether:
SqlConnection conn = new SqlConnection();
conn.ConnectionString = ConfigurationManager.ConnectionStrings["main"].ToString();
conn.Open();
string sql = "SELECT * FROM " + tableName;
SqlDataAdapter da = new SqlDataAdapter(sql, conn);
DataTable dtResult = new DataTable();
da.Fill(dtResult);
foreach (DataRow drRow in dtResult.Rows)
{
Console.WriteLine(drRow["Id"].ToString());
}
da.Dispose();
conn.Close();
conn.Dispose();
If you want the recordset, you can access the Connection property of the DataContext class (db variable in your context) and use it to execute regular query and get the result in either of DataTable or DataReader.
You can use the Dynamic Linq Query library (or D-Linq for short).
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Sorry - I'm away from a dev machine at the moment, but would this help?
It seems to suggest you should use DynamicQuery ...
Another way - as was mentioned by
several commenters in my previous post
- is to use DynamicQuery. DynamicQuery is one of the samples installed with
the 101 LINQ samples and you can find
it by clicking on Help | Samples in
Visual Studio. If you drill into the
sample folders there's a DynamicQuery
sample project, which basically
consists of a class that provides
string based lambda expression
parsing.
The class DynamicQuery class is self
contained and you can simply add it to
your project. It provides additional
extension methods that let you use
string expressions for various of the
query methods including the .Where()
method (but unfortunately for the
above example not the .Single()
method). So with Dynamic Query the
above .Load() method can also be
written as follows:
There's an actual code example on the post, too...
LINQ to SQL is meant to be strongly typed so I don't think you can use LINQ to SQL to use dynamic table names unless you use ExecuteQuery
Thanks
I don't think Dynamic Linq is the solution here.
As far as I know, there is no solution to your problem.
Even with dynamic linq, the compiler would need to somehow figure out what table the string refers to at compile time to allow strong typing of its members.
For instance, let's say you have two tables:
Product {Id, Name, Value}
Customer {Id, Firstname, Surname, Address, Email, ...}
And you use Linq-to-SQL as your ORM:
var items = from p in MagicTableResolver("Product")
where p.Firstname // <-- How could intellisense allow this?
select p;
var items = from c in MagicTableResolver("Customer")
where c.Name // <-- It can't, it cannot be strongly typed
select c;
Building off of this and this, here's how to run some some LINQ commands on a string tablename. I haven't figured out how to get the query syntax working (like "FROM" and "SELECT"), but you can still get and insert rows.
Type tableType = Assembly.GetExecutingAssembly().GetType("NameSpace.TableName");
ITable itable = dbcontext.GetTable(tableType);
//prints contents of the table
foreach (object y in itable) {
string value = (string)y.GetType().GetProperty("ColumnName").GetValue(y, null);
Console.WriteLine(value);
}
//inserting into a table
dynamic tableClass = Activator.CreateInstance(tableType);
//Alternative to using tableType
dynamic tableClass = Activator.CreateInstance(null, "NameSpace.TableName").Unwrap();
tableClass.Word = userParameter;
itable.InsertOnSubmit(tableClass);
dbcontext.SubmitChanges();
//sql equivalent
dbcontext.ExecuteCommand("INSERT INTO [TableName]([ColumnName]) VALUES ({0})", userParameter);

Categories

Resources