Using Dapper and Postgresql - citext data type - postgresql

I'm not sure if there is a way to support this, but I'm having trouble getting Dapper to map string parameter values to the Postgresql citext data type as it seems to be using the text type.
In particular, I'm trying to call a function that takes in citext parameters - the error I get back is:
var c = ConnectionManager<T>.Open();
string sql = #"select * from ""dbo"".""MyFunction""(#schemaName, #tableName);";
var param = new
{
schemaName = schema,
tableName = table
};
string insecureSalt = c.QueryMultiple(sql, param).Read<string>().FirstOrDefault();
ConnectionManager<T>.Close(c);
Error: Npgsql.PostgresException: 42883: function dbo.MyFunction(text, text) does not exist.
The signature that would match is function dbo.MyFunction(citext, citext) so clearly it can't find it using the default mapping.
According to Npgsql - http://www.npgsql.org/doc/types.html I need to be able to specify NpgsqlDbType.Citext as the type but I can't find a way to do this using Dapper.
Solved thanks to answer from Shay, complete solution here:
var c = ConnectionManager<T>.Open();
string sql = #"select * from ""dbo"".""MyFunction""(#schemaName, #tableName);";
var param = new
{
schemaName = new CitextParameter(schema),
tableName = new CitextParameter(table)
};
string insecureSalt = c.QueryMultiple(sql, param).Read<string>().FirstOrDefault();
ConnectionManager<T>.Close(c);
public class CitextParameter : SqlMapper.ICustomQueryParameter
{
readonly string _value;
public CitextParameter(string value)
{
_value = value;
}
public void AddParameter(IDbCommand command, string name)
{
command.Parameters.Add(new NpgsqlParameter
{
ParameterName = name,
NpgsqlDbType = NpgsqlDbType.Citext,
Value = _value
});
}
}

You probably need to create create a CitextParameter which extends ICustomQueryParameter. This API allows you to pass an arbitrary DbParameter instance to Dapper - in this case it would be an instance of NpgsqlParameter with its NpgsqlDbType set to Citext.
Something like this should work:
class CitextParameter : SqlMapper.ICustomQueryParameter
{
readonly string _value;
public CitextParameter(string value)
{
_value = value;
}
public void AddParameter(IDbCommand command, string name)
{
command.Parameters.Add(new NpgsqlParameter
{
ParameterName = name,
NpgsqlDbType = NpgsqlDbType.Citext,
Value = _value
});
}
}

When you write the SQL query, you can cast the parameter value like cast(#param as citext).
in my case below worked correctly. (usr is a class object)
string sql = "select * from users where user_name = cast(#user_name as citext) and password = #password;";
IEnumerable<users> u = cnn.Query<users>(sql, usr);
In your case, you can change the query like below and see if that works
string sql = #"select * from ""dbo"".""MyFunction""(cast(#schemaName as citext), cast(#tableName as citext));";

Related

EFCore DbConnection.CreateCommand parameters not passing

I'm using EFCore in my project, and I'm attempting to execute a raw SQL query (because I'm targeting a database that I don't own and don't want to scaffold out the massive schema for it), passing in a parameter. Here is what I'm doing (although I've changed the query for protection):
var customerId = "testname";
using (var command = _dbContext.Database.GetDbConnection().CreateCommand())
{
command.CommandText = #"select * from customers where customerName = '#customerName'";
var customerIdParam = new SqlParameter("#customerName", customerId);
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "#customerName";
parameter.Value = customerId;
parameter.DbType = System.Data.DbType.String;
parameter.Direction = System.Data.ParameterDirection.Input;
command.Parameters.Add(parameter);
await _dbContext.Database.OpenConnectionAsync();
using (var result = await command.ExecuteReaderAsync())
{
return result;
}
}
The problem is that the parameter doesn't seem to be passing in. If I hardcode the value for customerName that I'm sending in, it works just fine and returns rows, but if I pass it in as a parameter, result doesn't return any rows
The parameter must not be enclosed inside ', otherwise it is not detected as a parameter. Use this instead:
command.CommandText = #"select * from customers where customerName = #customerName";

GroupBy Clause: To be able to pass in a Column name as a string

In EF, Order By allows a column name to be used to eg OrderBy("Description")
I need to be able to do some thing similar with GroupBy
Other posts have solutions when the column type is known
var groupByExpressionGN2 = GetGroupByExpressionGuidNull<DebtWaiver>("PersonUID");
...
// in the query
.GroupBy(groupByExpression2)
// the Expression function
private static Expression<Func<TEntity,Guid?>> GetGroupByExpressionGuidNull<TEntity>(string property)
{
var item = Expression.Parameter(typeof(TEntity), "gb");
var itemProperty = Expression.PropertyOrField(item, property);
var lambda = Expression.Lambda<Func<TEntity, Guid?>>(itemProperty, item);
return lambda;
}
But my users may select 1 of any columns by which to group by
So how can I make the function above return an expression for group by
I have tried this:
public static Expression<Func<T, object>> GetMember<T>(string memberName)// where T : EntityObject
{
ParameterExpression pe = Expression.Parameter(typeof(T), "p");
System.Reflection.PropertyInfo pi = typeof(T).GetProperty(memberName);
return (Expression<Func<T, object>>)Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Property(pe, pi), typeof(object)), pe);
}
but it produces : p =>Convert(p.PersonUID)
instead of:p =>p.PersonUID
Regards
GregJF
After a bit more testing (and a good night's sleep) I got the second method to work (Thanks to JA Rreyes )
My issue was that I was using this:
var groupByExpressionGN2 = GetGroupByExpressionGuidNull<DebtWaiver>("PersonUID");
...
// in the query
.GroupBy(groupByExpression2)
I should have being doing this:
var groupByExpression2 = GetMember<DebtWaiver>("PersonUID");
...
// in the query
.GroupBy(groupByExpression2.Compile())
You can use the second method in my original post( GetMember ), but I use this method('cos I like it!): Thanks: Taher Rahgooy
public static Expression<Func<T, object>> GetPropertySelector<T>(string propertyName)
{
var arg = Expression.Parameter(typeof(T), "gb");
var property = Expression.Property(arg, propertyName);
var conv = Expression.Convert(property, typeof(object));
var exp = Expression.Lambda<Func<T, object>>(conv, new ParameterExpression[] { arg });
return exp;
}
Regards
GregJF

The specified cast from a materialized 'System.Byte[]' type to a nullable 'System.Byte' type is not valid

public virtual ObjectResult<Nullable<byte>> GetPersonalPicture3(Nullable<System.Guid> contactId)
{
var contactIdParameter = contactId.HasValue ?
new ObjectParameter("ContactId", contactId) :
new ObjectParameter("ContactId", typeof(System.Guid));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Nullable<byte>>("GetPersonalPicture3", contactIdParameter);
}
in the bottom Code, I Faced this error "The specified cast from a materialized 'System.Byte[]' type to a nullable 'System.Byte' type is not valid."
byte x = DBPSO.GetPersonalPicture3(ProfileID).FirstOrDefault() ?? 0000;
in addition, I've tested this Code too
var x = DBPSO.GetPersonalPicture3(ProfileID).Select(B => B.Value).ToArray();
string binaryPic = System.Text.Encoding.Unicode.GetString(x);
The picture on the db is probably a varbinary so an array of byte and not a single byte.
Try this:
public virtual ObjectResult<byte[]> GetPersonalPicture3(Nullable<System.Guid> contactId)
{
var contactIdParameter = contactId.HasValue ?
new ObjectParameter("ContactId", contactId) :
new ObjectParameter("ContactId", typeof(System.Guid));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<byte[]>("GetPersonalPicture3", contactIdParameter);
}

mvc4 expression tree for contains integer

I want to implement expression tree for integers as in below query:
Select * From TableName Where PKID In (1,2,3,4,5)
The relevant extracted code:
protected Expression<Func<T, bool>> GetExpression<T>(string propertyName, string propertyValue, string propertyType)
{
var parameter = Expression.Parameter(typeof(T), "type");
switch (propertyType)
{
case "string":
case "string?":
var stringProperty = Expression.Property(parameter, propertyName);
var stringValue = Expression.Constant(propertyValue.Trim(), typeof(string));
MethodInfo stringMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var stringContainsMethod = Expression.Call(stringProperty, stringMethod, stringValue);
return Expression.Lambda<Func<T, bool>>(stringContainsMethod, parameter);
case "int?":
var intProperty = Expression.Property(parameter, propertyName);
var intValues = Expression.Constant(propertyValue.Trim(), typeof(string));
MethodInfo intMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var intContainsMethod = Expression.Call(intProperty, intMethod, intValues); // this line gives error
return Expression.Lambda<Func<T, bool>>(intContainsMethod, parameter);
}
}
// Calling above function for string works. Products is a table in Database and Entity framework generated the class based on database first.
var filterLambda = GetExpression<Products>("ProductName", "chicken", "string?"); // No error here, works great
// Calling above function for int gives error
var filterLambda = GetExpression<Products>("ProductId", "1,2,3,4,5", "int?"); // Error here
I get error:
Method 'Boolean Contains(System.String)' declared on type 'System.String' cannot be called with instance of type 'System.Int32'
Of course it is pretty much clear that the column ProductId is integer and the value is string. Integer doesnt have contains so I thought I should use strings contains method. Please can any one tell me how do I solve this?

consume int result returned from stored procedure in MVC4

I have created stored procedure like this:
ALTER procedure [dbo].[takeOrder]
(
#id int = 0,
#userid int = 0,
#diningid int,
#amount float
)
as
begin
declare #countt as int
select #countt = COUNT(*)
from Order_master
where dining_Id = #diningid and isActive = 1
if #countt <> 0
begin
update Order_master
set amount = #amount
where dining_Id = #diningid and isActive = 1;
end
else
begin
insert into Order_master(userid, dining_Id, amount, [date], isActive)
values (#userid, #diningid, #amount, GETDATE(), 1)
end
--select amount from Order_master where dining_Id=#diningid and isActive=1
select oid
from Order_master
where dining_Id = #diningid and isActive = 1
end
In controller I am trying to get return result as mention below:
[HttpPost]
public ActionResult takeOrder(Order_master order,List<Menu_info> menu)
{
String msg = "";
int oid;
if (ModelState.IsValid)
{
try
{
Order_master objOreder = new Order_master
{
amount = order.amount,
isActive = 1,
date = order.date,
dining_Id = order.dining_Id,
userId = order.userId
};
object orderId = db.takeOrder(objOreder.oid, objOreder.userId, objOreder.dining_Id, objOreder.amount);
oid = (int)orderId;
msg = "success..!!";
}
catch (Exception ex)
{
msg = "error...";
}
}
else
{
msg = "please provide info";
}
if (Request.IsAjaxRequest())
{
return new JsonResult { Data = msg, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
else
{
return View();
}
}
But it returns an exception
Cannot convert type 'System.Data.Objects.ObjectResult' to 'int'
I am not able to get single return value from stored procedure.
Hope I get the right solution.
object orderId = db.takeOrder(objOreder.oid, objOreder.userId, objOreder.dining_Id, objOreder.amount);
oid = (int)orderId;
Try this conversion method.
oid = Convert.ToInt32(orderId);
You can use like
public int InsertWebProperty(int orgid, int? userid, string address, string property_Id, string latitude, string longitude, int? compamyid)
{
return Convert.ToInt32((db.InsertWebProperty(orgid, userid, address, property_Id, latitude, longitude, 0)).FirstOrDefault().recidentity.Value);
}
Please let me know if it not worked for you..
Thanks
db.TakeOrder().FirstOrDefault().ToString() = One value returned as a string
db.TakeOrder().FirstOrDefault().ToList() = Row returned as a list.
Create view as
CREATE VIEW [dbo].[vScalar]
AS
SELECT '' AS Value
In Stored Procedure return
SELECT CAST(orderId AS VARCHAR(10)) AS value
In Entity Framework, set function return use entity vScalar
In code
vScalar orderId = db.takeOrder(objOreder.oid, objOreder.userId, objOreder.dining_Id, objOreder.amount).FirstOrDefault;
oid = (int)orderId.value;