Empty TVP in Dapper - dapper

I have created a procedure that receives some primitives parameters and one TVP.
I have created a library that receives a List<T> and returns a List<SqlDataRecord> for the TVP (https://github.com/Kunze/EasyTVP). And it works, except when my TVP is empty/null.
When the tvp is null I get (becausa TVP cannot be null in sql server):
"Table Valued Parameter cannot be null"
But TVP works if you dont pass the parameter if it's null, so I tried to use a expando like this (the same error for DynamicParameters):
dynamic expando = new ExpandoObject();
...
if(model.Products != null && model.Products.Count> 0){
expando.Products = list_of_sql_data_record;
}
but the Query method of dapper throws this error (it works if it is a class, but not for dynamic):
{"The member of type Microsoft.SqlServer.Server.SqlDataRecord cannot be used as a parameter value"}
I can get this work if I create 2 classes, one for Products.Count > 0 and one different for Products = null or 0, but this is ridiculous, right?.
So, How can I make this works?
conexao.Query(#"Add", new
{
model.Name,
Products = list_of_sql_data_record //this may be empty
}, commandType: System.Data.CommandType.StoredProcedure);

I could do it works calling AsTableValuedParameter:
if(model.Products != null && model.Products.Count > 0){
dynamicParameters.Add("Products", list_of_sql_data_record.AsTableValueParameter());
}

Related

Unable to get the returned value while calling Stored Procedure from JPA

The SP looks like:-
CREATE PROCEDURE <spnamegoeshere>
AS
BEGIN
/* Some code goes here */
return 102323 /*Need to get this value*/
END
GO
We are executing a Stored Procedure (hosted in SQL SERVER), through JPA using
createStoredProcedureQuery("spnamegoeshere");
I need to get that returned value "102323". Unfortunatly, using above command, unable to get.
After searching a bit, got this link.
https://learn.microsoft.com/en-us/sql/connect/jdbc/using-a-stored-procedure-with-a-return-status?view=sql-server-2017
As per the above doc, I can able to get the value, if I use .
CallableStatement
Any way to do the same using the JPA, without modifying the SP?
Could someone share some input here.
This works for me:
#Test
public void testProc() {
SimpleJdbcCall call = new SimpleJdbcCall(jdbcTemplate);
Map<String, Object> map = call.withProcedureName("retValTestProc").withReturnValue().execute();
Assert.assertNotNull("Expected not null result but it is", Objects.nonNull(map));
Assert.assertTrue("Expected size = 1, actual=" + map.size(), map.size() == 1);
Assert.assertTrue("Expected value=102323, actual=" + map.get("RETURN_VALUE"),
Objects.equals(102323, map.get("RETURN_VALUE")));
}
Where retValTestProc = <spnamegoeshere>
Also you can create a SimpleJdbcCall with a DataSource constructor

Passing a full-text search parameter using Dapper.net

Consider this simple query which use full text searching on the Keywords field:
DECLARE #searchTerm VARCHAR(500) = 'painted'
SELECT * FROM StockCatalogueItems
WHERE (CONTAINS(KeyWords, #searchTerm))
This works as expected, but I need to do the same using a Dapper.net parameterised query. When using stored procedures, I create the full text parameter like this: "\"painted*\""
But using the same approach this doesn't work using dapper. No results are returned. This is the line in the query where I use the parameter:
AND (CONTAINS(KeyWords, #search))
and it's passed to the query like so:
return _context.Database.Connection.Query<StockProfileMatrix>(basequery, new
{
search = searchTerm
}
I can only assume that dapper is sanitising the string somehow, removing quotes perhaps?
Any ideas?
This works for me. However the tech stack am working on is .net core RTM and "Dapper": "1.50.0-rc3",
_dbConnection.QueryAsync<Guid>(#"select br.Id from Brand br where CONTAINS(br.Text,#Name)",new {Name = $"\"*{name}*\""}))
For completeness, I'll answer the question. The query syntax is correct, but the way in which the full-text parameter is created was obviously not. I created an extension method that formats the parameter:
public static string ToFullText(this string str)
{
string searchTerm = null;
if (!string.IsNullOrEmpty(str))
{
string[] keywords = str.Trim().Split(null);
foreach (var keyword in keywords)
{
searchTerm += string.Format("\"{0}*\" AND ", keyword);
}
if (searchTerm != null)
searchTerm = searchTerm.Substring(0, searchTerm.LastIndexOf(" AND "));
}
return searchTerm;
}
Then I call the method when I pass the parameter in to the dapper query:
_context.Database.Connection.Query<dynamic>(query, new
{
search = filter.SearchTerm.ToFullText()
});

Variable in filter query in Jaydata

I have a string which I need to pass in filter query of Jaydata.Kindly guide..
Here is my following code:
var string = ((id=4 || id>6)&& (Name contains 'a'));
mydb.Document.filter(function(result){
return result.str;
}).toArray(function(abc){
console.log(abc);
});
Here mydb is sQlite db instance name and Document is table name.
Error comes when I use result.str as str is not the field name of the table.
How can I do this.
your filter is not correct, also you can pass parameter with referring the second parameter, like this
mydb.Document.filter(function(result){
return result.str == this.foo;
}, { foo: 'bar'}).toArray(function(abc){
of course, instead of 'bar' you can pass any value

LINQ to SQL Data Context logs don't show the WHERE clause

The following is the C# code and generated SQL in a LINQ to SQL query for two cases.
Case 1
using (JulianDataContext dc = new JulianDataContext(this.CurrentConnectionString))
{
#if DEBUG
dc.Log = new DebugTextWriter();
#endif
IEnumerable<UserNewsfeedDeliveryTime> temp = dc.UserNewsfeedDeliveryTimes.Where(u => u.NewsfeedEmailPeriodicity > 0 && DateTime.Today >= u.NextNewsfeedDelivery.Value.Date);
ids = temp.Select(p => p.Id).ToList();
}
SELECT [t0].[Id], [t0].[NewsfeedEmailPeriodicity], [t0].[LastSentNewsfeedEmail], [t0].[NextNewsfeedDelivery]
FROM [dbo].[UserNewsfeedDeliveryTimes] AS [t0]
WHERE ([t0].[NewsfeedEmailPeriodicity] > #p0) AND (#p1 >= CONVERT(DATE, [t0].[NextNewsfeedDelivery]))
-- #p0: Input Int (Size = -1; Prec = 0; Scale = 0) [0]
-- #p1: Input DateTime (Size = -1; Prec = 0; Scale = 0) [15-11-2012 00:00:00]
Case 2
using (JulianDataContext dc = new JulianDataContext(this.CurrentConnectionString))
{
#if DEBUG
dc.Log = new DebugTextWriter();
#endif
IEnumerable<UserNewsfeedDeliveryTime> temp = dc.GetTable<UserNewsfeedDeliveryTime>();
temp = temp.Where(u => u.NewsfeedEmailPeriodicity > 0 && DateTime.Today >= u.NextNewsfeedDelivery.Value.Date);
ids = temp.Select(p => p.Id).ToList();
}
SELECT [t0].[Id], [t0].[NewsfeedEmailPeriodicity], [t0].[LastSentNewsfeedEmail], [t0].[NextNewsfeedDelivery]
FROM [dbo].[UserNewsfeedDeliveryTimes] AS [t0]
The difference
The difference between these two linq queries:
dc.UserNewsfeedDeliveryTimes
and
dc.GetTable<UserNewsfeedDeliveryTime>()
Why? Could it be that, in case 2, LINQ to SQL is retrieving all data from database and finish the query by filtering all objects in memory?
If so, how can we make keep this generic and still force all the T-SQL to be generated?
Solution
Both answers, are correct but I had to pick one, sorry! I think also it is interesting to add that in this case, since I changed to work with an IQueryable (inherits from IEnumerable), in this line:
temp = temp.Where(u => u.NewsfeedEmailPeriodicity > 0 && DateTime.Today >= u.NextNewsfeedDelivery.Value.Date);
I had two overload methods, one from the IQueryable interface and another to the IEnumerable interface.
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
So I had to convert my predicate explicitly to Expression> predicate, otherwise the IEnumerable interface method would have been picked up at compile time and, if I am not mistaken, I would get some dynamic sql exception saying the T-SQL could not have been generated.
From my understanding, IEnumerable does not transform the original query information that IQueryable holds. It's almost as if the cast freezes any changes to the IQueryable query at the point of casting. If you look at MSDN, it turns out that IQueryable inherits IEnumerable:
http://msdn.microsoft.com/en-us/library/system.linq.iqueryable.aspx
Hence, you see this behaviour. It is important with LINQ-SQL to work with IQueryable unless you want the query frozen at the point it is turned to an IEnumerable.
In your first example, the where is inclusive of the original query. The select is not hence the query generated.
In your second example, you capture the table itself into an IEnumerable. Any changes on top of this are done in memory on top of the original query.
When you think, the IEnumerable version of where will not be able to transform the original data of the IQueryable due to the cast and how inheritance works.
When you also consider deferred loading, and how LINQ works, this seems to make sense.
To me it is a big annoyance, as it can lead you into generating some terrible performing code.
Try using IQueryable instead of IEnumerable.
Weird, because on my examples I get the opposite results from you, ie with IEnumerable, case 1 works fast and case 2 retrieves all the data. But using IQueryable fixes the issue.

Custom query with Castle ActiveRecord

I'm trying to figure out how to execute a custom query with Castle ActiveRecord.
I was able to run simple query that returns my entity, but what I really need is the query like that below (with custom field set):
select count(1) as cnt, data from workstationevent where serverdatetime >= :minDate and serverdatetime < :maxDate and userId = 1 group by data having count(1) > :threshold
Thanks!
In this case what you want is HqlBasedQuery. Your query will be a projection, so what you'll get back will be an ArrayList of tuples containing the results (the content of each element of the ArrayList will depend on the query, but for more than one value will be object[]).
HqlBasedQuery query = new HqlBasedQuery(typeof(WorkStationEvent),
"select count(1) as cnt, data from workstationevent where
serverdatetime >= :minDate and serverdatetime < :maxDate
and userId = 1 group by data having count(1) > :threshold");
var results =
(ArrayList)ActiveRecordMediator.ExecuteQuery(query);
foreach(object[] tuple in results)
{
int count = (int)tuple[0]; // = cnt
string data = (string)tuple[1]; // = data (assuming this is a string)
// do something here with these results
}
You can create an anonymous type to hold the results in a more meaningful fashion. For example:
var results = from summary in
(ArrayList)ActiveRecordMediator.ExecuteQuery(query)
select new {
Count = (int)summary[0], Data = (string)summary[1]
};
Now results will contain a collection of anonymous types with properties Count and Data. Or indeed you could create your own summary type and populate it out this way too.
ActiveRecord also has the ProjectionQuery which does much the same thing but can only return actual mapped properties rather than aggregates or functions as you can with HQL.
Be aware though, if you're using ActiveRecord 1.0.3 (RC3) as I was, this will result in a runtime InvalidCastException. ActiveRecordMediator.ExecuteQuery returns an ArrayList and not a generic ICollection. So in order to make it work, just change this line:
var results = (ICollection<object[]>) ActiveRecordMediator.ExecuteQuery(query);
to
var results = (ArrayList) ActiveRecordMediator.ExecuteQuery(query);
and it should work.
Also note that using count(1) in your hql statement will make the query return an ArrayList of String instead of an ArrayList of object[] (which is what you get when using count(*).)
Just thought I'd point this out for the sake of having it all documented in one place.

Resources