简体   繁体   中英

C# .NET What's the best way to organize SQL queries and have clean code

me and my colleague we face with an issue. We have couple of SQL queries as strings.Here's an example:

    public class Query
{

    public static string CreditTransferId(string expectedValue, string table, int statusId, int messageTypeId, int destination103, int destination202, string StoreStatus202Id)
    {
        return $"SELECT top 1 Id from {table} where MessageId = '{expectedValue}' and FlowId=3 and StatusId={statusId} and MessageTypeId={messageTypeId} and " +
          $" Destination103={destination103} and Destination202={destination202} and StoreStatus103Id is null and StoreStatus202Id {StoreStatus202Id}";
    }
}

For now we have them returned as strings from methods inside the Query class. We want to refactor the code make it clean, since we have a method with more than 3 parameters which is pretty hard to use anyways.

How would you go about this? What's the cleanest way to organize SQL queries which needs a lot of parameters like we have in the code above? I'll really appreciate any answers that might improve the current state of our code.

Thanks!

Dynamic SQL is a very bad thing for a start as these are open to SQL injection, you should use parameterise queries and return a string.

"eg: SELECT top 1 Id from [Table] where [MessageId] = @messageId" etc

So you dont need to pass in any values, you would add these to your list of SqlParamater's

The table name is probably pointless as this is related to the sql, so probably just add that into the sql string

This doesn't really need an extra class, just create the sql variable where you call it, so it right there if you need it?

..or use Stored Procedures

..or use Entity Framework

EF is great and you have to decide if that's what you want. There are some cases where it is not suitable. Of u decide to stick with plain text queries how about dividing queries into parts: FromBuilder JoinBuilder GroupBuilder Condition builder etc

ex.:

return @"
"+new TableIdSelectorBuilder(table).Get() +@"
"+new FromBuilder().Get(table) +@"
"+new TableConditionByIdBuilder(table).Get(I'd)+@"
";

EDIT: Stored procedures allow to change queries without publishing new app version but is a bit pain in the ass to work on a living organism. At least sometimes.

Hopefully this helps you a bit. I was figuring this out a long time ago too.

Use nameOf instead of hardcoded column names

One advice that I have is: Try to use nameOf() for column names (in case your domain model matches exactly your database structure).

Refactoring becomes a lot easier as well.

Create a query builder

I created a class which allows me to create simple to medium complex queries (Select, Insert, Update, Joins, Deletes, Limit, Offset, ...). For most of the queries that I write I can use this query builder to get what I need and the code will look something like this:

var query = new QueryBuilder()
        .Update(nameof(MyTable))
        .Set(nameof(MyTable.Name), entity.Name)
        .Set(nameof(MyTable.Firstname), entity.Firstname)
        .WhereEquals(nameof(MyTable.Id), entity.Id)
        .Build();

Unfortunately, I haven't found a good QueryBuilder out there yet. Therefore I created my own.

Use string literals

Microsoft Documentation

Another solution that I recently encountered is by doing the following:

var query = $$"""
        SELECT * FROM {{nameof(MyTable)} 
        WHERE
        XX 
    """;

This at least allows you to indent correctly and the code will remain clean.

Outsource your queries in separate files

I was also thinking about having bigger queries in separate files and include them in the Assembly. When you want to execute the query, you load the query from the file and run it like this.

You might also want to cache that query then, to not always load it from the file.

Final words

  • Personally, I try to use the QueryBuilder as often as possible. It creates clean code which looks somewhat like LINQ
  • Backup approach for me is to use the string literals as showed above
  • Trying to create separate files for queries, I have never tried yet, because there was no use for myself
  • I would also avoid using Stored Procedures, because they are pain to write and also hard to debug in my opinion
  • The cleanest option would be to use Entity Framework, but if you want to use Micro-ORMs like Dapper, try one of the solutions above

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM