I was trying to use the Where and OrWhere methods of SqlBuilder for Dapper, but it is not acting like how I would expect.
The edited portion of this question is basically what I ran into. Since it didn't receive a response, I'll ask it here.
var builder = new SqlBuilder();
var sql = builder.AddTemplate("select * from table /**where**/ ");
builder.Where("a = #a", new { a = 1 })
.OrWhere("b = #b", new { b = 2 });
I expected select * from table WHERE a = #a OR b = #b
but I got select * from table WHERE a = #a AND b = #b
Is there any way to add an OR to the where clause using the SqlBuilder?
I think it's just a matter of changing the following in the SqlBuilder class to say OR instead of AND, but I wanted to confirm.
public SqlBuilder OrWhere(string sql, dynamic parameters = null)
{
AddClause("where", sql, parameters, " AND ", prefix: "WHERE ", postfix: "\n", IsInclusive: true);
return this;
}
Nevermind. I looked through the SqlBuilder code and found that if there is a mixture of Where and OrWhere, it will do the following:
Join all the AND clauses
Join all the OR clauses separately
Attach the OR clauses at the end of the AND clauses with an AND
If you don't have more than 1 OrWhere, then you won't see any OR.
I'll modify my query logic to take this into account
You have to change your query into:
var builder = new SqlBuilder();
var sql = builder.AddTemplate("select * from table /**where**/ ");
builder.OrWhere("a = #a", new { a = 1 })
.OrWhere("b = #b", new { b = 2 });
In case you want to try another alternative, DapperQueryBuilder may be easier to understand:
var query = cn.QueryBuilder($#"
SELECT *
FROM table
/**where**/
");
// by default multiple filters are combined with AND
query.FiltersType = Filters.FiltersType.OR;
int a = 1;
int b = 2;
query.Where($"a = {a}");
query.Where($"b = {b}");
var results = query.Query<YourPOCO>();
The output is fully parametrized SQL (WHERE a = #p0 OR b = #p1).
You don't have to manually manage the dictionary of parameters.
Disclaimer: I'm one of the authors of this library
Related
What I'm trying to do it have a filter object that is populated like so
var filter = new Filter
{
ThingID = 1,
Keywords = new[] { "op", "s" }
};
And then be able to build up the query like this:
var sb = new StringBuilder();
sb.AppendLine("select * from Stuff where 1=1");
if (filter.ThingID.HasValue)
{
sb.AppendLine(" and ThingID = #ThingID");
}
if (filter.Keywords != null)
{
for (int i = 0; i < filter.Keywords.Length; i++)
{
string keyword = filter.Keywords[i];
if (!string.IsNullOrWhiteSpace(keyword))
{
sb.AppendLine(" and ( Model like '%' || #Keywords" + i + " || '%' )");
}
}
}
var sql = sb.ToString();
var results = Query<Stuff>(sql, filter).ToList();
This works fine if just the ThingID prop is populated, but as far as I can tell Dapper is not feeding the Keywords in as a parameter in any way. Is this possible with Dapper, or does it only work in the context of " where Keywords in #Keywords"?
Parameters need to match by name; you are adding parameters like #Keywords17, but there is no Keywords17 property for it to add. It doesn't interpret that as Keywords[17], if that is what you mean. There is some automatic expansion of flat arrays, but that is intended for expanding in #Keywords (although the expansion itself is not specific to in). There is not currently something that would help you automatically there; I would suggest DynamicPaameters instead:
var args = new DynamicParameters();
args.Add("ThingID", 1);
...
if (!string.IsNullOrWhiteSpace(keyword))
{
sb.AppendLine(" and ( Model like '%' || #Keywords" + i + " || '%' )");
args.Add("Keywords" + i, keyword);
}
...
var cmd = new CommandDefinition(sql, args, flags: CommandFlags.NoCache);
var results = Query<Stuff>(cmd).AsList();
note the subtle two changes at the end here - CommandFlags.NoCache will help avoid building lots of lookup entries for similar but different SQL (although you might choose to pay this to reduce per-item cost, up to you). The AsList instead of ToList avoids an extra list allocation.
I am attempting to build a dynamic Sql query for multiple search terms. I understand in general how to use the builder, but am not sure what to do in the loop since I actually need the #term to be different each time (I think). Not just in the query, but in the anonymous type as well to match.
I could use a string.Format in the query string, but not sure how to match it in the anonymous type?
public async Task<List<Thing>> Search(params string[] searchTerms)
{
var builder = new SqlBuilder();
var template = builder.AddTemplate("SELECT * /**select**/ from ThingTags /**where**/ ");
for (int i = 0; i < searchTerms.Length; i++)
{
builder.OrWhere("value LIKE #term", new { term = "%" + searchTerms[i] + "%" });
}
...
}
in the current form the query that gets created for terms "abc" "def" "ghi" is
CommandType: Text, CommandText: SELECT * from ThingTags WHERE ( value LIKE #term OR value LIKE #term OR value LIKE #term )
Parameters:
Name: term, Value: %ghi%
Well here is one way to do the query building. I didn't realize that the parameters could be a Dictionary initially.
public async Task<List<Thing>> Search(params string[] searchTerms)
{
var builder = new SqlBuilder();
var template = builder.AddTemplate("SELECT * /**select**/ from ThingTags /**where**/ ");
for (int i = 0; i < searchTerms.Length; i++)
{
var args = new Dictionary<string, object>();
var termId = string.Format("term{0}", i.ToString());
args.Add(termId, "%" + searchTerms[i] + "%");
builder.OrWhere("value LIKE #" + termId, args);
}
...
}
You can easily create that dynamic condition using DapperQueryBuilder:
var query = cn.QueryBuilder($#"
SELECT *
FROM ThingTags
/**where**/");
// by default multiple filters are combined with AND
query.FiltersType = Filters.FiltersType.OR;
foreach (var searchTerm in searchTerms)
query.Where($"value like {searchTerm}");
var results = query.Query<YourPOCO>();
The output is fully parametrized SQL (WHERE value like #p0 OR value like #p1 OR...). You don't have to manually manage the dictionary of parameters.
Disclaimer: I'm one of the authors of this library
I want to create a query against a database with a for statement (in C#)
something like this:
List<object> data = new List<object>();
for(int i = 0; i < executeScalar("SELECT COUNT(*) FROM mytable"); i++)
{
List[i] = executeRead("SELECT rownumber(i) From mytable");
// or
executeUpdate("UPDATE mytable SET ... inrownumber(i)",List[i])
}
and the question is: is there any function to use for this "rownumber(i)" and "inrownumber(i)"?
I know I can do it like this
List[i] = executeRead("SELECT * From mytable WHERE ROW_NUMBER() = " + i);
and
executeUpdate("UPDATE mytable SET ... WHERE ROW_NUMBER() = " + i,List[i])
but if I do that - the database will search in all the table each time to find one item, so if I have 100 items, the database will pass on 10,000 items. and I wont that each time the database go directly to the row, so it pass only 100 items in all the for statement
Do you know any way do do it?
(I need it because in my program - the developer assumed that all the data is in the list, and he take them with a for statement and by index, and do "Add" and "Insert" and so on, and I don't wont to change all the program)
Thanks
Assuming you have your data stored in a generic list called places then
Using (SqlConnection cn = GetMyDbConnectionHere())
{
Using(SqlCommand cmd = new SqlCommand("dbo.UpdatePlace", cn)
{
// Create your parameters for the command here - e.g. p_PlaceName
foreach(Place place in places)
{
if(place.HasChanged)
{
p_PrimaryKey.value = place.primaryKey;
p_PlaceName.value = place.placeName;
p_PlaceLat.value = place.lat;
// And so on and so forth
cmd.ExecuteNonQuery();
}
}
}
}
All this code is straight off the top of my head and typed directly into SO on the web page - so I make no guarantee as to it being fully functional - but it should at least get you going... In addition there's zero error handling here - also a major no-no.
I have seen QueryMultiple from Dapper official doc as below, It is convenient!
var sql = #"
select * from Customers where CustomerId = #id
select * from Orders where CustomerId = #id
select * from Returns where CustomerId = #id";
using (var multi = connection.QueryMultiple(sql, new {id=selectedId}))
{
var customer = multi.Read<Customer>().Single();
var orders = multi.Read<Order>().ToList();
var returns = multi.Read<Return>().ToList();
...
}
Now, when I delete record from Parent-table,I want to delete related record from Child-table.
can Dapper fit it? It looks that as below.
var sql = #"delete from tb_role where roleid=#ID
delete from tb_rolepermission where roleid=#ID
delete from tb_userrole where roleid=#ID
";
var param = new { ID=id };
connection.EXECUTEMultiple(sql, param)..........
Any help will be appreciated!
Yes, you can can simply call connection.Execute and it already allows multiple commands like you are trying to do, the same as ExecuteQuery allows on SqlCommand, which is all Dapper is calling anyways.
I am trying to determine a way to audit which records a given user can see by;
Object Type
Record Type
Count of records
Ideally would also be able to see which fields for each object/record type the user can see.
We will need to repeat this often and for different users and in different orgs, so would like to avoid manually determining this.
My first thought was to create an app using the partner WSDL, but would like to ask if there are any easier approaches or perhaps existing solutions.
Thanks all
I think that you can follow the documentation to solve it, using a query similar to this one:
SELECT RecordId
FROM UserRecordAccess
WHERE UserId = [single ID]
AND RecordId = [single ID] //or Record IN [list of IDs]
AND HasReadAccess = true
The following query returns the records for which a queried user has
read access to.
In addition, you should add limit 1 and get from record metadata the object type,record type, and so on.
I ended up using the below (C# using the Partner WSDL) to get an idea of what kinds of objects the user had visibility into.
Just a quick'n'dirty utility for my own use (read - not prod code);
var service = new SforceService();
var result = service.login("UserName", "Password");
service.Url = result.serverUrl;
service.SessionHeaderValue = new SessionHeader { sessionId = result.sessionId };
var queryResult = service.describeGlobal();
int total = queryResult.sobjects.Count();
int batcheSize = 100;
var batches = Math.Ceiling(total / (double)batcheSize);
using (var output = new StreamWriter(#"C:\test\sfdcAccess.txt", false))
{
for (int batch = 0; batch < batches; batch++)
{
var toQuery =
queryResult.sobjects.Skip(batch * batcheSize).Take(batcheSize).Select(x => x.name).ToArray();
var batchResult = service.describeSObjects(toQuery);
foreach (var x in batchResult)
{
if (!x.queryable)
{
Console.WriteLine("{0} is not queryable", x.name);
continue;
}
var test = service.query(string.Format("SELECT Id FROM {0} limit 100", x.name));
if(test == null || test.records == null)
{
Console.WriteLine("{0}:null records", x.name);
continue;
}
foreach (var record in test.records)
{
output.WriteLine("{0}\t{1}",x.name, record.Id);
}
Console.WriteLine("{0}:\t{1} records(0)", x.name, test.size);
}
}
output.Flush();
}