GithubHelp home page GithubHelp logo

drizin / dapperquerybuilder Goto Github PK

View Code? Open in Web Editor NEW
480.0 14.0 49.0 288 KB

Dapper Query Builder using String Interpolation and Fluent API

License: MIT License

C# 98.01% TSQL 0.64% PowerShell 1.35%
dapper querybuilder dapper-query-builder interpolated-strings

dapperquerybuilder's People

Contributors

dependabot[bot] avatar drizin avatar erenguler avatar frankyjquintero avatar jehhynes avatar ksmith173 avatar larsstegman avatar mullon avatar terryaney avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dapperquerybuilder's Issues

Temp table not exists

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!

Not able to use interpolation for column names and query operators

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

No need for FormattableString in OrderBy

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.

How to use fluent api to build /**filters**/

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?

[Question] FluentQueryBuilder: How to pass TableName dynamically?

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.

Documentation: QueryBuilder should be FluentQueryBuilder

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");

Support for MySql, Sqlite , and Oracle

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.

QueryBuilder IN lists query parameter overlap with other parameter

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!

DapperQueryBuilderOptions.AutoGeneratedParameterPrefix is not used consistently

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.

Array expansion

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.

@Drizin @jehhynes Do you have any suggestions?

[MySQL] How to pass LIMIT and OFFSET?

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

[comment/kudos] this is amazing.

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.

Pass Column Names

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

In statement formatting

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
})

Using dapper built in methods with query builder

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 breaks queries

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

AppendLine doesn't appear to be working

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):

image

Security

Why on earth would u do this this is just as bad as string.format interpulation is open to sql inejection

Can't call QueryBuilder

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;
//...

image

See my csproj:
image

Thank you in advance.

Dynamic column names

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!

AppendLine too slow when AppendLine 8000 more line

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

Feature: Editor Config

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.

PascalCase for a table name?

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?

Duplicate Parameters

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?

image

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.

Missing parentheses in example

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;

Major Rewrite (v2.x)

I've been considering a major rewrite for a while, for many reasons:

  • Current version is too coupled with Dapper, it's barely reusable
  • Too difficult to customize or modify anything. Bad design decisions, I guess
  • Merging SQL statements is just too ugly and the way we're doing regex conversion back and forth is not efficient. The idea is that we should use a standard format for the underlying string and only do the conversion (and give names to the numbered args) by the moment that we need to invoke Dapper.
  • net6.0+ support InterpolatedStringHandlers which don't even require regex parsing

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.

string interpolation during instantiation isn't working.

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?

Unit Testing for IFromBuilder

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

Automatic reuse of duplicated parameters

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

Error with QueryMultiple using a stored procedure

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?

Bulk insert list of objects

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.

Use QueryBuilder Multiple Times

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?

Dapper Multi Mapping

Thanks for this lovely application.

How does DapperQueryBuilder work with Dapper's multi mapping? Is it supported?

Performance issue

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

How to add MySQL dynamic LIMIT clause?

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

Support Filters initializer with IFilter arguments

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.