Dapper return specfic field from query - dapper

I am working on connecting to SQL database and setting variables in my C# application to match returned values from a Dapper Query.
I am able to return the correct row information (I used a datagridview to show that i get the correct row, and when i debug i see the right data) but how do I set a program variable to just one of the columns? here is some code showing my process
Connect & run stored procedure:
using (SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MfgDataCollector"].ToString()))
{
DynamicParameters param = new DynamicParameters();
param.Add("#UserID", txt_userid.Text.Trim());
List<User> userinfo = conn.Query<User>("GetUserInfo", param, commandType: CommandType.StoredProcedure).ToList<User>();
Variables.UserID = txt_userid.Text.Trim();
datagridview1.DataSource = userinfo; //this displays the right information, however I want to store the information as a public variable(like above)
Variables.Userfull = userinfo; //when debugging this shows I have the right information but its all columns of user
User Class:
class User
{
public string UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int AccessLevel { get; set; }
public int FirstUse { get; set; }
public string Password { get; set; }
public int LoginCounter { get; set; }
public string LastLogin { get; set; }
public string FirstLogin { get; set; }
public string UserFullName { get; set; }
}
EDIT
per answer, I have tried to switch the command to executescalar with this code:
var userinfo = conn.ExecuteScalar<User>("GetUserInfo", param, commandType: CommandType.StoredProcedure);
This allows me to set
Variables.UserID = userinfo.UserID;
without red-squiggle lines however during run-time i get an error on the execute scalar of "System.InvalidCastException: 'Invalid cast from 'System.String' to 'Name_Space.User'.'
I have checked the User Class to ensure the data type matches with the Database and i see no problems there, I'm not sure what I'm doing wrong?

If you want value from only one column of only one row, use ExecuteScalar which is similar to ADO.NET ExecuteScalar. It will return object with which you have to deal further.
string sql = "SELECT COL1 FROM Table1 WHERE ID = 1";
//OR
//string sql = "SELECT TOP 1 COL1 FROM Table1";
//OR similar
object colValue = conn.ExecuteScalar(sql, ....);
If matching record not found, return value will be null.
Check Dapper documentation for other generic variants of method:
public static T ExecuteScalar<T>(this IDbConnection cnn, CommandDefinition command);
If you want single column from multiple rows, the way you are doing this now is correct. Just modify your SQL query/Stored Procedure to return the same. Dapper will only map returned column. All other properties in your User will remain unassigned.
If you simply want to assign values to Variables.UserID instead of binding those using DataSource, then just do that like:
Variables.UserID = userinfo.UserID;
Variables.Userfull = userinfo.UserFullName;
//and so on....
So, complete code will be something like below:
using(SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MfgDataCollector"].ToString()))
{
DynamicParameters param = new DynamicParameters();
param.Add("#UserID", txt_userid.Text.Trim());
User userinfo = conn.Query<User>("GetUserInfo", param, commandType: CommandType.StoredProcedure).ToList<User>().First();
Variables.UserID = userinfo.UserID;
Variables.Userfull = userinfo.UserFullName;
}

Related

How do I call stored procedures in EF Core 6 using named parameters with FromSqlRaw

I want to execute a stored procedure with a minimum of 3 to 20 parameters.
I am converting the property values from a request model to an SQL string and a list of parameters. Then make the call and expect a result but when I check the Created SQL from debug view, I observe that the EF tries to call the sp with non-named style parameters.
For brevity assume the first 3 properties contain values, and other properties have null values.
RequestModel
{
string Property1 { get; set; }
string Property2 { get; set; }
string Property3 { get; set; }
...
}
ResponseModel
{
string Response1 { get; set; }
string Response2 { get; set; }
...
}
var sqlString = "exec sp_dummy #prop1, #prop2, #prop3";
var params = new SqlParameter[] { new SqlParameter("#prop1", Property1), new SqlParameter("#prop2", Property2), new SqlParameter("#prop3", Propery3) };
return await ResponseModel.FromSqlRaw(sqlString, params).ToListAsync();
And when I check the generated SQL from EF Core, it is as follows:
DECLARE #prop1 nvarchar(10) = N'Prop1Data';
DECLARE #prop2 nvarchar(10) = N'Prop2Data';
DECLARE #prop3 nvarchar(10) = N'Prop3Data';
exec dummy_sp #prop1, #prop2, #prop3
What I want to achieve is as follows:
exec dummy_sp #prop1 = N'Prop1Data' , #prop2 = N'Prop2Data', #prop3 = N'Prop3Data'
I have achieved this by using dbcontext's ExecuteSqlRaw method, instead of FromSqlRaw extension method.
Database.ExecuteSqlRaw(sql,params);

How can I implement this T-SQL in my stored procedure?

I'm new to SQL Server and want to implement this scenario. My stored procedure gets 8 input parameters from a C# web application, and checks all input has into the table. For that purpose I wrote this simple stored procedure:
CREATE PROCEDURE CheckValid
#p_bank varchar,
#p_pay_date varchar,
#p_bill_id varchar,
#p_payment_id varchar,
#p_ref_code varchar,
#p_branch varchar,
#p_channel_type varchar,
#p_send_date varchar
AS
BEGIN
SELECT
[p_bank], [p_pay_date], [p_bill_id], [p_payment_id],
[p_ref_code], [p_branch], [p_channel_type], [p_send_date]
FROM
[SAMPLE].[dbo].[MixedTable]
WHERE
[p_bank] = #p_bank
AND [p_pay_date] = #p_pay_date
AND [p_bill_id] = #p_bill_id
AND [p_payment_id] = #p_payment_id
AND [p_ref_code] = #p_ref_code
AND [p_branch] = #p_branch
AND [p_channel_type] = #p_channel_type
AND [p_send_date] = #p_send_date
END
But want to return to c# application this scenario, for example c# sends all field but when stored procedure select run for this purpose can not find data, for example p_bill_id not correct into the table for more explain in select query into where clause in the [p_bill_id]=#p_bill_id not trust and now want to return sp this :
p_bill_id,not found
and other example c# all variable correct but two field [p_channel_type] and [p_payment_id] not correct into where clause but other 6 field correct now SP return this:
[p_channel_type],not found
[p_payment_id],not found
Summary of question:
When data for passed parameter value is not found, I want it to return that corresponding column.
For example:
[p_channel_type],not found
[p_payment_id],not found
Note, varchar means varchar(1) so you should specify length for each argument explicitly like varchar(100)
CREATE PROCEDURE CheckValid
#p_bank varchar(<length>),
#p_pay_date varchar(<length>),
#p_bill_id varchar(<length>),
#p_payment_id varchar(<length>),
#p_ref_code varchar(<length>),
#p_branch varchar(<length>),
#p_channel_type varchar(<length>),
#p_send_date varchar(<length>)
AS
BEGIN
if not exists(select 1 from dbo.BankTable where p_bank = #p_bank)
begin
raiserror('Bank %s not found', 16, 1, #p_bank)
return
end
if not exists(select 1 from dbo.BillTable where p_bill_id = #p_bill_id)
begin
raiserror('Bill %s not found', 16, 1, #p_bill_id)
return
end
...
SELECT [p_bank],[p_pay_date],[p_bill_id],[p_payment_id],[p_ref_code],[p_branch],[p_channel_type],[p_send_date]
FROM [SAMPLE].[dbo].[MixedTable]
where [p_bank]=#p_bank and [p_pay_date]=#p_pay_date
and [p_bill_id]=#p_bill_id and [p_payment_id]=#p_payment_id
and [p_ref_code]=#p_ref_code and [p_branch]=#p_branch
and [p_channel_type]=#p_channel_type and [p_send_date]=#p_send_date
END
GO
Instead of creating stored procedure for this move "validation" logic to your c# application.
Database is just IO device and I think keeping "business logic" in IO device not a good approach.
// Class which represent your eight parameters
public class Data
{
public string Bank { get; set; }
public string PayDate { get; set; }
public string BillId { get; set; }
public string PaymentId { get; set; }
public string RefCode { get; set; }
public string Branch { get; set; }
public string ChannelType { get; set; }
public string SendDate { get; set; }
}
public class Validation
{
private Data _data;
public Validation(Data data)
{
_data = data;
}
public IEnumerable<string> Validate()
{
var columns = new KeyValuePair<string, string>[]
{
new KeyValuePair("p_bank", _data.Bank),
new KeyValuePair("p_pay_date", _data.PayDate),
new KeyValuePair("p_bill_id", _data.BillId),
new KeyValuePair("p_payment_id", _data.PaymentId),
new KeyValuePair("p_ref_code], _data.RefCode),
new KeyValuePair("p_branch", _data.Branch),
new KeyValuePair("p_channel_type", _data.ChannelType),
new KeyValuePair("p_send_date", _data.SendDate)
};
return columns.Where(pair => IsValueExists(pair.Key, pair.Value) == false);
}
private bool IsValueExists(string columnName, string value)
{
var query =
$"SELECT [{columnName}]
FROM [SAMPLE].[dbo].[MixedTable]
WHERE [{columnName}] = #value";
var parameter = new SqlParameter
{
ParameterName = "#value",
SqlDbType = SqlDbType.VarChar,
Value = _data.Bank
};
using (var connection = new SqlConnection(yourConnectionString))
using (var command = new SqlCommand(query, connection))
{
command.Parameters.Add(parameter);
connection.Open();
var value = command.ExecuteScalar();
return value != null; // null returned if no rows exists
}
}
}
Then you can use this method somewhere
var data = new Data { Bank = "BankName", RefCode = "SomeRefcode" } // put all values
var validation = new Validation(data);
var invalidValues = validation.Validate();
foreach(var invalidValue in invalidValues)
{
// Print or save column names where value is invalid
}

Value is not a convertible object

I have a simple query and Poco that I'm using with Dapper like so:
var jc = this.dbConnection.ExecuteScalar<JcUser>("SELECT loginid as Username,Password,coalesce(CustomerId,0) as CustomerId,TextProfileId,UxProfileId from \"user\" where id = #id", new {id = id});
Poco:
public class JcUser
{
public string UserName { get; set; }
public string Password { get; set; }
public int CustomerId{ get; set; }
public int TextProfileId { get; set; }
public int UxProfileId { get; set; }
}
When this executes it throws an exception with the message
Value is not a convertible object: System.String to JcUser
The stack trace ends up at: at System.Convert.ToType (System.Object value, System.Type conversionType, IFormatProvider provider, Boolean try_target_to_type)
Any ideas why its doing this?
Thanks
UPDATE: Using var jc = this.dbConnection.Query<JcUser>("SELECT loginid as Username,Password,coalesce(CustomerId,0) as CustomerId,TextProfileId,UxProfileId from \"user\" where id = #id", new {id = id}).First(); appears to work. I also realise I'm a moron and ExecuteScalar is only for one value. However, is my update the best way to retrieve only one row?
ExecuteScalar maps to the ADO.NET method of the same name. It returns at most one cell: one grid, one row, one column. As such, it is not intended for use with complex objects, and cannot work correctly in your case as you have multiple columns.
Dapper assumes you would only use that with simple types like int, string etc.
In your case, use:
var jc = this.dbConnection.Query<JcUser>(
sql, args).SingleOrDefault();
If you want to avoid a hidden List<> allocation you could also pass buffered: false.

Mapping fields in stored procedure to class properties with different names with Dapper

I have this exert from a POCO class with many fields:
public class Call
{
public int Id { get; set; }
public string Customer { get; set; }
public int StatusId { get; set; }
public int UserAssignedToId { get; set; }
public string UserAssignedToName { get; set; }
}
However my stored procedure returns different names to the properties above (in this case the Id is before:
IdCall
IdStatus
IdUserAssignedTo
This is the code I am using to execute the stored procedure:
var call = conn.Query<Call>("CallSPName", new { IdCall = callId }, commandType: CommandType.StoredProcedure).First();
How can I specify a mapping to say I would like "IdStatus" from my stored procedure map to "StatusId" in my POCO class and "IdCall" to "CallId" etc?
I don't have access to change the stored procedures as they are controlled by DBAs and older legacy systems are using them which would break if the fields got changed in the stored procedure.
Any ideas/thoughts appreciated.
The closest thing which comes to my mind is to have private properties mapped to columns returned by the stored procedure and make the public properties with the names you want setting and getting those private fields:
// ...
private int IdStatus;
public int StatusId {
get { return IdStatus; }
set { IdStatus = value; }
}
// ...

.Net WCF RIA Services parameterized NameValue method crashing

I added a RIA Domain Service method to return a simple NameValuePair of two properties from a table (and filtered on a key value).
It compiles fine, but blows up every time without giving a useful error.
What am I missing? (probably something really obvious)
e.g.:
public IQueryable<NameValuePair> GetNameValues(int keyId)
{
// NOTE: I can breakpoint here and the correct keyId is passed
// it blows up on returning from this method
return from p in this.ObjectContext.NameTable
where p.KeyId == keyId
select new NameValuePair(p.NameValue, p.NameType);
}
Simple NameValuePair Code:
public class NameValuePair
{
[Key]
public string Name { get; set; }
public string Value { get; set; }
public NameValuePair()
{
}
public NameValuePair( string name, string value)
{
this.Name = name;
this.Value = value;
}
}
Update:
I tried returning a query on a static list of NameValuePair objects and that works fine (but is not useful).
I tried this here and got the error: base {System.SystemException} = {"Only parameterless constructors and initializers are supported in LINQ to Entities."}
So you have to change it to create the object first, then pass the property values:
public IQueryable<NameValuePair> GetNameValues(int keyId)
{
return from p in this.ObjectContext.NameTable
where p.KeyId == keyId
select new NameValuePair {Name = p.NameValue, Value = p.NameType};
}

Resources