drizin / dapperquerybuilder Goto Github PK
View Code? Open in Web Editor NEWDapper Query Builder using String Interpolation and Fluent API
License: MIT License
Dapper Query Builder using String Interpolation and Fluent API
License: MIT License
Hi there! I found strange situation,the last code will throw exception #tmp table not found.
using SqlConnection conn = new SqlConnection(connectionString);
conn.Open();
int i = 0;
QueryBuilder builder = conn.QueryBuilder($@"
if 1={i}
begin
select 'exit'
return
end
create table #tmp(id nvarchar(50));");
var yy= builder.Execute();
builder = conn.QueryBuilder($"insert into #tmp(id) values('test')");
var rst = builder.Execute(); // <--- throws exception
Thank you!
Hi
I am trying to parameterize the column names and query operator as I want to write some dynamic queries as given below
var searchQuery= cn.QueryBuilder($@"SELECT columnA
FROM table A
/**where**/");
searchQuery.Where($" {q.field} {q.Operator} {q.Value}");
but above query doesnot execute .
but if I rewrite 2nd statement like below , the query executes
searchQuery.Where($" HairTypeEnum = {q.Value}");
Let me know if this is an issue
Hi, in the IWhereBuilder, in the IOrderByBuilder OrderBy(FormattableString orderBy);
method, there is no need to receive a FormattableString since order by never receives parameters. if you change it to IOrderByBuilder OrderBy(string orderBy);
, we would not need to add ':raw' suffixes when building a dynamic orderby clause.
Hello,
How to use /**filters**/
to build dynamic conditions when using fluent api?
Thanks
Use QueryBuilder
var filters = new Filters(Filters.FiltersType.AND);
filters.Add(new Filters()
{
new Filter($"Weight >= {20}"),
new Filter($"Weight <= {30}")
});
var builder2 = new QueryBuilder(null, $@"SELECT ProductId, Name, ListPrice, Weight
FROM [Production].[Product]
WHERE 1=1 /**filters**/
ORDER BY ProductId");
builder2.Where(filters);
Console.WriteLine(builder2.Sql);
Output
SELECT ProductId, Name, ListPrice, Weight
FROM [Production].[Product]
WHERE 1=1 AND (Weight >= @p0 AND Weight <= @p1)
ORDER BY ProductId
It works great!
Use FluentQueryBuilder
var builder = new FluentQueryBuilder(null);
builder.Select($"ProductId");
builder.Select($"Name");
builder.Select($"ListPrice");
builder.Select($"Weight");
builder.From($"[Production].[Product]");
builder.Where($"1=1");
builder.Where($"{filters}"); //the filters defined in the above code
Console.WriteLine(builder.Sql);
output
SELECT ProductId, Name, ListPrice, Weight
FROM [Production].[Product]
WHERE 1=1 AND @parray0
The filters
were replaced with @parray0
?
Found issues for From method as follow:
`
Dim q As FluentQueryBuilder = DbConnection.FluentQueryBuilder()
Dim ProductId As Integer = 1
Dim TableName As String = "products"
With q
.Select($"ProductId")
.Select($"ProductName")
.From($"{TableName}")
.Where($"ProductId = {ProductId}")
.OrderBy($"ProductName")
End With
Debug.WriteLine(q.Sql)
Dim p As List(Of Product) = q.Query(Of Product)()
`
Result:
SELECT ProductId, ProductName
FROM @p0
WHERE ProductId = @p1
ORDER BY ProductName
If I set fixed string values then everything is working well.
`
Dim q As FluentQueryBuilder = DbConnection.FluentQueryBuilder()
With q
.Select($"ProductId")
.Select($"ProductName")
.From($"Products")
.Where($"ProductId = 1")
.OrderBy($"ProductName")
End With
Debug.WriteLine(q.Sql)
Dim p As List(Of Product) = q.Query(Of Product)()
`
Result:
SELECT ProductId, ProductName
FROM Products
WHERE ProductId = 1
ORDER BY ProductName
Also If I set fixed string value of table name and use where part as dynamic value then also is working.
`
Dim q As FluentQueryBuilder = DbConnection.FluentQueryBuilder()
Dim ProductId As String = 1
With q
.Select($"ProductId")
.Select($"ProductName")
.From($"Products")
.Where($"ProductId = {ProductId}")
.OrderBy($"ProductName")
End With
Debug.WriteLine(q.Sql)
Dim p As List(Of Product) = q.Query(Of Product)()
`
Result:
SELECT ProductId, ProductName
FROM Products
WHERE ProductId = @p0
ORDER BY ProductName
Guess only issue is From method if you want to use it for table soruce.
There are a couple of instances in the readme using QueryBuilder instead of FluentQueryBuilder, e.g.:
var q = cn.QueryBuilder()
.SelectDistinct($"ProductId, Name, ListPrice, Weight")
.From("Product")
.Where($"ListPrice <= {maxPrice}")
.Where($"Weight <= {maxWeight}")
.Where($"Name LIKE {search}")
.OrderBy("ProductId");
Please some of us would be glad to know whether your package (Dapper Query Builder) supports for MySql, Sqlite , and Oracle.
These flavors of sql have slight changes in query formation.
Hello,I have a script replace by 6 length c# list var through by In lists query,the parameter have wrong place in action script,it overlap with other parameter,3 in list parameters was rewrite by other paramater value of 6 parameter!The script have total about 24 paramaters interpolation value,
for example:where departmentid p2->this list contain 6 element number then will create p21,p22,p23,p24,25,p26 names
and below script have multiple parameters,and also have name p21,p22,p23 too.
so can you name in clause not set begin with p,with other charactor.
Please forgive me my poor English!
Please help me ! Thank you very much!
Where you have a custom AutoGeneratedParameterPrefix set (not @) then if you have more than one paramter in a where clause they all get named p0.
This is because the @ symbol is hard-coded in the ParameterInfos.MergeParameters method.
I started to fix it but kept seeing other instances where the @ symbol is hard-coded, so I gave up, I don't have time to look through the whole codebase.
Also, what is with the AutoGeneratedParameterObjectPrefix? This looks like it does the same thing as AutoGeneratedParameterPrefix.
First of all, thanks a lot for maintaining this great library.
We're using MariaDB with Dapper & DapperQueryBuilder and currently having some issues with array expansion. Dapper expands each element in the IN
query like col IN (@p1, @p2)
but DapperQueryBuilder just leaves it like col IN @parray1
, which doesn't work for MariaDB/MySQL.
When a LIMIT or a LIMIT and OFFSET are included in the query, the query builder builds SQL that isn't valid. See:
var limit = 10;
var offset = 20;
var q = cn.QueryBuilder($@"SELECT Name FROM [Production].[Product] LIMIT {limit} OFFSET {offset}");
Console.WriteLine(q.Sql);
This generate the following SQL query:
SELECT Name FROM [Production].[Product] LIMIT @p0 OFFSET @p1
When this query is run, the result is a SQL syntax error because user defined variables cannot be used in a LIMIT clause. https://dev.mysql.com/doc/refman/8.0/en/user-variables.html
Not sure exactly how many goats were sacrificed in the building of this library, but just wow.
Thanks so much for writing it. I learned about how a $"" is actually an instance of FormattableString. Amazing.
How do we pass the column names dynamically to the Where method. Filters is a dictionary with key being the name of the column and Value is the value of the filter, it can be a list of strings, single string. I would like to do something like this, of-course this is pseudo code, in production I will check if the column exists in the database
foreach (var filter in filters)
{
q.Where($"{filter.Key}= @{filter.Value}");
}
Thanks in Advance
Hey. I am wondering if i am missing something, but i can't get array parameters to be formatted like so:
private readonly List<string> _someValues = new List<string>()
{
"value1",
"value2",
"value3"
};
var formattedValues = _someValues.Select(type => $"{type:varchar(50)}").ToList(); // <-- doesn't work because interpolated string expression is not IFormattable
var query = connection.QueryBuilder("select something from somewhere";
query += @$"where table.values IN {formattedValues}";
The only solution i can think of is this:
var formattedValues = _someValues.Select(type => new DbString
{
Value = type,
IsFixedLength = true,
Length = 50,
IsAnsi = true
})
before this package i was using Query multi mapping in the following manner:
connection.QueryAsync<BookingViewModel, UserViewModel, OperatorViewModel, BookingViewModel>(
sqlQuery,
(booking, user, bboperator) =>
{
booking.BookedByUser = user;
booking.BookedByOperator = bboperator;
return booking;
},
parms,
splitOn: "BookingId, UserId, OperatorId");
with the queryBuilder i would like to use the same with dapper support:
queryBuilder.QueryAsync<BookingViewModel, UserViewModel, OperatorViewModel, BookingViewModel>(
(booking, user, bboperator) =>
{
booking.BookedByUser = user;
booking.BookedByOperator = bboperator;
return booking;
},
splitOn: "BookingId, UserId, OperatorId");
Is it something possible in this package?
Append mess up query parameters:
var cn = new SqlConnection();
var qb = cn.QueryBuilder($"{ "A" }");
qb.Append($"{ "B" }");
Generated SQL query is expected to be: @p0 @p1
Actual: @p0 @p0
=> A A
Issue exist at least on 1.07 - 1.09
var qb = cn.QueryBuilder( $@"
INSERT[ Log ]( Action, FolderName, FileName, VersionID, VersionIndex, [User], Size )
SELECT 'D', fo.[Name], fi.[Name], ve.VersionID, ve.VersionIndex, {user}, ve.Size
FROM Version ve
INNER JOIN [File] fi ON fi.UID = ve.[File]
INNER JOIN Folder fo ON fo.UID = fi.Folder
WHERE ve.[File] = {parameters.Id}" );
if ( parameters.Soft )
{
qb.AppendLine( $"UPDATE [File] SET Deleted = 1 WHERE UID = {parameters.Id}" );
}
else
{
qb.AppendLine( $"UPDATE [File] SET Deleted = 1 WHERE UID = {parameters.Id}" );
}
Given the code above, I should have one INSERT into Log table and one UPDATE on File table. But if I stop on a breakpoint and look at qb.Sql
, I see the following (note that Parameters
appears to be correct):
Why on earth would u do this this is just as bad as string.format interpulation is open to sql inejection
Hello,
First of all it's my real first issue as a developer so please be easy on me. ๐
So I'm trying to use DapperQueryBuilder on its last version (1.3.0) but I can't call QueryBuilder on my Oracle connection context.
See the code bellow:
using DapperQueryBuilder;
//...
Thank you in advance.
Hi, the lib is really good!
Is multimapping supported?
Hey! Thanks for the great library and write-ups.
Is there any recommended strategy for handling dynamic column names?
For example, I'd like to be able to do this:
static Filter FilterOn(string column, object value) => new Filter($"[{column}] = {value}");
But obviously this won't work, as Filter will pick up {column}
and interpret it as a parameter.
The best I've been able to come up with is manually creating a Formattable string, but that's quite ugly.
Thoughts? Thanks!
Hi there! Thank your library! But AppendLine too slow when AppendLine 8000 more lines .Each appended script line was very simple. Scipt line like this: insert into (A,B) select {var1:raw},{var2:raw}.It take about 30 seconds
You should export your coding conventions into an editor config file and then include that in the root of your repository so when people help code, your coding conventions are applied instead of outside styles.
I use a postgres database.
I tried the below code. It did not work.
var schema = "sample";
var table = "Test2";
var sql = connection.CommandBuilder($@"
INSERT INTO {schema:raw}.{table:raw} (name)
VALUES ({test_name})
";
The error message was relation "sample.test2" does not exist
Does it not support any capital letters for a table name?
Wondering if there would be a way for you to detect if params in a query/interpolated string are identical and if so, don't add another param with duplicated value, but use the previously created parameter's name?
In above case, 3 null
parameters could be reduced to one (I think, maybe has to be conditional on type?), 3 dates
down to 1, 2 'string dates' down to one and two 'terry.aney' params down to one. So 12 parameters instead of 18 in this case.
I had written a query building library for another project that did something similar and it allowed a major reduction in parameters. Although, that library was building extremely large queries in a StringBuilder
and appending queries over and over, so hundreds of parameters. So with the way QueryBuilder is built, not exactly the same scenario (unless you allow for an AppendQuery at some point). Thought I would mention it in case it was an easy change.
In your example below there are missing parentheses at the end before semicolon
string productName = "%Computer%";
int subCategoryId = 10;
var products = cn
.QueryBuilder($@"
SELECT * FROM Product
WHERE
Name LIKE {productName}
AND ProductSubcategoryID = {subCategoryId}
ORDER BY ProductId"
).Query;
I've been considering a major rewrite for a while, for many reasons:
As a first draft, I've created this fresh project which is just a FormattableString
implementation, but it's like a StringBuilder since it supports Append, Replace, Insert, etc: https://github.com/Drizin/InterpolatedStrings.
The next major release of DapperQueryBuilder will be based on this new library. (I guess major parts of CommandBuilder will be delegated to this other library).
Feel free to share your feedback.
Hi there,
I have a situation where I have dynamic tables / views that share the same structure. For this reason I need to dynamically name the table. When I instantiate the querybuilder as seen below the "{sessionId}" gets translated to "@p0"
var qry = conn.QueryBuilder($@"select * from d_common.TrackData_{sessionId}_v where 1=1");
It appears that the parameter part of the Formattablestring is not making it to the query.
This line:
var results = qry.Query().ToList();
Yields:
"42601: syntax error at or near "@""
qry.sql = "select * from d_common.TrackData_@p0_v where 1=1"
qry.parameters does include my sessionId
Am I using it wrong or is this a bug?
Delete it
I'm trying to write a unit test case for my repository method and I'm just getting error which is quite an issue.
All I'm saying is
IEnumerable<AppUserPersistent>? appUser = new List<AppUserPersistent>() { fixture.Create<AppUserPersistent>() };
_testUnitWork
.Setup(x => x.UserRepository.GetListByFluentBuilderAsync(It.IsAny<IFromBuilder>()))
.ReturnsAsync(appUser);
I'm getting below error
IRepository ... => ....GetListByFluentBuilderAsync(It.IsAny()):
This setup was not matched.
My repository method
public Task<IEnumerable> GetListByFluentBuilderAsync(IFromBuilder? builder)
{
return builder.QueryAsync(_transaction);
}
Could you please let me know what's wrong with above code
Is reuse of dubplicated parameters broken ? The code from manual
string productName = "Computer";
var query = conn.QueryBuilder($"SELECT * FROM Product WHERE Name = {productName} OR Category = {productName}");
generates following sql:
SELECT * FROM Product WHERE Name = @p0 OR Category = @p1
Hello!
I've been tracking this issue for a while, and got some insight that can help. Internally, you're using a Dictionary
via the ParameterInfos
class to store the parameters. This has some problems when executing a stored procedure that returns multiple result sets, as referenced in this Dapper issue. As per the most recent comment, if you instead use DynamicParameters
instead of a Dictionary
, the problem is avoided.
Would it be possible to use the DynamicParameters object instead of a dictionary when sending the command to Dapper?
Hello,
When im using dapper with the following query:
DECLARE @TempTable TABLE(
ID int not null,
Name nvarchar(255) not null
)
INSERT INTO @TempTable VALUES (@ID, @Name)
and c# code
public class TestClass
{
public int ID { get; set; }
public string Name { get; set; }
}
var params = new List<TestClass>(); // add objects
using Dapper;
... conn.Execute(sql, params)
dapper manages to resolve list and insert all objects into the temp table.
Now i'm trying to use feature of QueryBuilder with this approach but without success.
Any advise how can use list insert with QueryBuilder ?
Thanks,
Nikolay.
Is it possible to use a qb multiple times?
var qb = cn.QueryBuilder( $"SELECT * FROM Foo /**where**/" );
var fileKey = 12;
var files = qb.Where( $"Bar = {fileKey}" ).QueryAsync<ListFile>();
fileKey = 13
files = qb.Where( $"Bar = {fileKey}" ).QueryAsync<ListFile>();
Very simply query, but if I have a complex query and I don't want to write it twice. Obviously, I could break it out into separate method, but wondering if something like this is possible all within one method?
Thanks for this lovely application.
How does DapperQueryBuilder work with Dapper's multi mapping? Is it supported?
Hello everyone. Thanks for your work.
This is my first issue, so don't get me wrong.
I'm in love with fluent code style, but i've a huge perfomance issue using the DapperQueryBuilder.
TLDR: ~6k ms overhead for querying using this library.
Am I doing somthing wrong?
UPD: Sorry, I've found it is a parameter sniffing problem. Might be solved appending OPTION(RECOMPILE) to the end of query
var stopWatch = new Stopwatch();
stopWatch.Start();
var fluentSingle = await DbConnection.FluentQueryBuilder()
.Select($"*").From($"api.vwReserves").Where($"Id = {reserveId}").QuerySingleAsync<vwReserve>();
stopWatch.Stop();
var fluentSingleElapsed = stopWatch.ElapsedMilliseconds; //~6k ms
stopWatch.Reset();
stopWatch.Start();
var simpleBuilder = await DbConnection.QueryBuilder($@"SELECT * FROM api.vwReserves WHERE Id = {reserveId}")
.QuerySingleAsync<vwReserve>();
stopWatch.Stop();
var simpleBuilderElapsed = stopWatch.ElapsedMilliseconds; //~6k ms
stopWatch.Reset();
stopWatch.Start();
var dapperQuery = await DbConnection.QuerySingleAsync<vwReserve>($"SELECT * FROM api.vwReserves WHERE Id = {reserveId}");
stopWatch.Stop();
var dapperElapsed = stopWatch.ElapsedMilliseconds; // ~15ms
Someone sent me a private email with this question:
Thanks for your repo.
I have some question about it , how can I do the limit with this one ?var q = cn // SQL .QueryBuilder($@"select a.*,b.cus_alias from shp_bah a left join cus_cus b on b.cus_nbr=a.cus_nbr /**where**/ limit {qs.size} ");
SQL :
select a.*,b.cus_alias from shp_bah a left join cus_cus b on b.cus_nbr=a.cus_nbr /**where**/ limit @p0
Cuz this will be syntax error with mysql.
Thanks.
Lin ******@gmail.com
I have a class Source
that contains a field IDictionary<string, string> Tags
. I want to create Filters based on these tags. I would like to be able to initialize the filter like this:
new Filters(Filters.FiltersType.OR, source.Tags.Select(t =>
new Filters(Filters.FiltersType.AND)
{
new Filter($"TAGS.[Key] = {t.Key}"),
new Filter($"TAGS.Value = {t.Value}")
}
)
);
This requires a new initializer on Filters:
public Filters(FiltersType type, IEnumerable<IFilter> filters);
The public Filters(FiltersType type, params FormattableString[] filters)
initializer can then just call the new one.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.