SQL CLR stored procedure output - sql

there is a simple class called User and List of its objects
public class User
{
public int ID;
public string UserName;
public string UserPassword;
}
...
List userList = new List();
Can i make this list of User objects as result of execution SLQ CLR stored procedure ?
e.g. i want to get this
ID UserName UserPassword
1 Ted SomePassword
2 Sam Password2
3 Bill dsdsd
[SqlProcedure]
public static void GetAllocations()
{
// what here ??
}
P.S. Please do not advice me to use Sql functions. It does not suit me because it does not support output parameters
P.S.2 i will be very appreciated for any help !

Try to create a virtual table with SqlDataRecord and send it over the Pipe property of SqlContext object:
[SqlProcedure]
public static void GetAllocations()
{
// define table structure
SqlDataRecord rec = new SqlDataRecord(new SqlMetaData[] {
new SqlMetaData("ID", SqlDbType.Int),
new SqlMetaData("UserName", SqlDbType.VarChar),
new SqlMetaData("UserPassword", SqlDbType.VarChar),
});
// start sending and tell the pipe to use the created record
SqlContext.Pipe.SendResultsStart(rec);
{
// send items step by step
foreach (User user in GetUsers())
{
int id = user.ID;
string userName = user.UserName;
string userPassword = user.UserPassword;
// set values
rec.SetSqlInt32(0, id);
rec.SetSqlString(1, userName);
rec.SetSqlString(2, userPassword);
// send new record/row
SqlContext.Pipe.SendResultsRow(rec);
}
}
SqlContext.Pipe.SendResultsEnd(); // finish sending
}

Related

How to send Message to User id Saved in AspNetUsers Table Signal R

I am trying to send a message. I have tried to connection id
public Task SendMessaageToConnectionID(string ConnectionID,string Message)
{
return Clients.Clients(ConnectionID).SendAsync("RecieveMessage", Message);
}
it is successfully done
Now I am trying this
public Task SendMessageToUser(string userId,string Message)
{
return Clients.Clients(userId).SendAsync(Message);
}
I am sending the user id of user Saved in AspNetUser Table
How Can I send this to a User ID or is there any other way except connection id to send the message to user?
SignalR won't store the UserId-ConnectionId mappings for us. We need to do that by our own. For example, when some user sets up a connection to the Hub, it should trigger a ReJoinGroup() method.
In addition, in order to make sure the Groups property works fine, you need also :
invoke RemoveFromGroupAsync to remove the old <connectionId, groupName> mapping
invoke AddToGroupAsync to add a new <connectionId, groupName> mapping.
Typically, you might want to store these information in Redis or RDBMS. For testing purpose, I create a demo that stores these mappings in memory for your reference:
public class MyHub:Hub
{
/// a in-memory store that stores the <userId, connectionId> mappings
private Dictionary<string, string> _userConn = new Dictionary<string,string>();
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
public override async Task OnConnectedAsync()
{
// get the real group Name by userId,
// for testing purpose, I use the userId as the groupName,
// in your scenario, you could use the ChatRoom Id
var groupName = Context.UserIdentifier;
await this.ReJoinGroup(groupName);
}
// whenever a connection is setup, invoke this Hub method to update the store
public async Task<KeyValuePair<string,string>> ReJoinGroup(string groupName)
{
var newConnectionId = Context.ConnectionId;
var userId = Context.UserIdentifier;
await this._semaphore.WaitAsync();
try{
if(_userConn.TryGetValue(userId, out var oldConnectionId))
{
_userConn[userId]= newConnectionId;
// remove the old connectionId from the Group
if(!string.IsNullOrEmpty(groupName)){
await Groups.RemoveFromGroupAsync(oldConnectionId, groupName);
await Groups.AddToGroupAsync(newConnectionId, groupName);
}
} else {
_userConn[userId]= newConnectionId;
if(!string.IsNullOrEmpty(groupName)){
await Groups.AddToGroupAsync(newConnectionId, groupName);
}
}
} finally{
this._semaphore.Release();
}
return new KeyValuePair<string,string>(userId, newConnectionId);
}
/// your SendMessageToUser() method
public async Task SendMessageToUser(string userId,string Message)
{
// get the connectionId of target user
var userConn = await this.GetUserConnection(userId);
if( userConn.Equals(default(KeyValuePair<string,string>))) {
throw new Exception($"unknown user connection with userId={userId}");
}
await Clients.Clients(userConn.Value).SendAsync(Message);
}
/// a private helper that returns a pair of <UserId,ConnectionId>
private async Task<KeyValuePair<string,string>> GetUserConnection(string userId)
{
KeyValuePair<string,string> kvp = default;
string newConnectionId = default;
await this._semaphore.WaitAsync();
try{
if(this._userConn.TryGetValue(userId, out newConnectionId)){
kvp= new KeyValuePair<string, string>(userId, newConnectionId);
}
} finally{
this._semaphore.Release();
}
return kvp;
}
}

Search where A or B with querydsl and spring data rest

http://localhost:8080/users?firstName=a&lastName=b ---> where firstName=a and lastName=b
How to make it to or ---> where firstName=a or lastName=b
But when I set QuerydslBinderCustomizer customize
#Override
default public void customize(QuerydslBindings bindings, QUser user) {
bindings.bind(String.class).all((StringPath path, Collection<? extends String> values) -> {
BooleanBuilder predicate = new BooleanBuilder();
values.forEach( value -> predicate.or(path.containsIgnoreCase(value) );
});
}
http://localhost:8080/users?firstName=a&firstName=b&lastName=b ---> where (firstName=a or firstName = b) and lastName=b
It seem different parameters with AND. Same parameters with what I set(predicate.or/predicate.and)
How to make it different parameters with AND like this ---> where firstName=a or firstName=b or lastName=b ??
thx.
Your current request param are grouped as List firstName and String lastName. I see that you want to keep your request parameters without a binding, but in this case it would make your life easier.
My suggestion is to make a new class with request param:
public class UserRequest {
private String lastName;
private List<String> firstName;
// getters and setters
}
For QueryDSL, you can create a builder object:
public class UserPredicateBuilder{
private List<BooleanExpression> expressions = new ArrayList<>();
public UserPredicateBuilder withFirstName(List<String> firstNameList){
QUser user = QUser.user;
expressions.add(user.firstName.in(firstNameList));
return this;
}
//.. same for other fields
public BooleanExpression build(){
if(expressions.isEmpty()){
return Expressions.asBoolean(true).isTrue();
}
BooleanExpression result = expressions.get(0);
for (int i = 1; i < expressions.size(); i++) {
result = result.and(expressions.get(i));
}
return result;
}
}
And after you can just use the builder as :
public List<User> getUsers(UserRequest userRequest){
BooleanExpression expression = new UserPredicateBuilder()
.withFirstName(userRequest.getFirstName())
// other fields
.build();
return userRepository.findAll(expression).getContent();
}
This is the recommended solution.
If you really want to keep the current params without a binding (they still need some kind of validation, otherwise it can throw an Exception in query dsl binding)
you can group them by path :
Map<StringPath,List<String>> values // example firstName => a,b
and after that to create your boolean expression based on the map:
//initial value
BooleanExpression result = Expressions.asBoolean(true).isTrue();
for (Map.Entry entry: values.entrySet()) {
result = result.and(entry.getKey().in(entry.getValues());
}
return userRepository.findAll(result);

looking for a good example to create a db record with dapper/sproc

Can you point me to a good example for creating a db record with Dapper via sproc? Ideally I would take an OO approach similar to EF like this:
var user = new User{ FirstName="John", LastName="Smith"}
var userId = dapper.Create(User);
return userId;
Of course, I'm open to the "right" way of doing this in dapper if dapper recommends a different approach
Assuming you have a stored procedure to add a record to the User table like
CREATE PROCEDURE SaveUser(#firstName VARCHAR(10),#lastName VARCHAR(10))
AS
BEGIN
INSERT INTO [User] (FirstName,LastName) VALUES (#firstName ,#lastName);
SELECT SCOPE_IDENTITY()
END
Assuming your User table has Id column (PK) set to Identity.
Now, you can execute your stored procedure like this
var conStr="Replace your connection string here";
using (var con = new SqlConnection(conStr))
{
var userId = con.Query<int>("SaveUser",
new {#firstName = "John", #lastName = "Smith"},
commandType: CommandType.StoredProcedure
).First();
}
If you have a class to represent the User table record, you can pass that class object when you call the stored proc.
public class UserDto
{
public string FirstName { set;get;}
public string LastName { set;get;}
}
and simply pass the object of this!
var conStr="Replace your connection string here";
var u = new UserDto { FirstName = "Johny", LastName= "Depp" };
using (var con = new SqlConnection(conStr))
{
var userId = con.Query<int>("SaveUser", u, commandType: CommandType.StoredProcedure)
.First();
}
You might also want to have a look at Drapper (built on top of Dapper) which would allow you to do something like this.
// instance of user created somewhere & procedure called
// on repository method.
var user = new User { FirstName = "John", LastName = "Smith" };
_commander.Execute(user) ? user : null;
Assuming you're using a repository class of some sort. I've taken the liberty of fleshing out a bit more code, adding some constructor injection love.
public class UserRepository : IUserRepository
{
// the IDbCommander is a Drapper construct
private readonly IDbCommander _commander;
public UserRepository(IDbCommander commander)
{
_commander = commander;
}
public User Create(User user)
{
// execute your procedure here (looked up in config)
return _commander.Execute(user) ? user : null;
}
public IEnumerable<User> RetrieveAll()
{
return _commander.Query<User>();
}
}
Your SQL code would then be saved in a separate file (json or xml config).
This keeps your C# separate from your SQL making life easier in the long run - changes to your SQL don't involve recompiling your code, your repository class becomes more testable, etc. etc. etc.
{
"Namespaces": [
{
"Namespace": "Company.Product.Namespace",
"ConnectionString": {
"ProviderName": "System.Data.SqlClient",
"ConnectionString": "Data Source=(LocalDb)\\mssqllocaldb;Initial Catalog=DrapperTests;Integrated Security=true"
},
"Types": [
{
"Name": "Company.Product.Namespace.UserRepository",
"Commands": {
"Create": {
"CommandText": "[dbo].[usp_CreateUser]",
"CommandType": 4
},
"RetrieveAll": {
"CommandText": "select * from [User];",
"CommandType": 1
}
}
}
]
}
]
}

Automatically mapping output parameters with Dapper

I've been using Dapper to call stored procedures passing it an object. For example:
If I have an object:
public int ID { get; set; }
public int Year { get; set; }
I can create this object and pass it to my Execute call as the parameters. Dapper automatically maps all of those properties into parameters and executes the stored procedure. Awesome.
What about output parameters? If my object looked like the following how can I get Dapper to populate that property with the output parameter value?
public int ID { get; set; }
public int Year { get; set; }
public int OutputParameter { get; set; }
Do output parameters have to be added as DynamicParameters?
Something like this:
DynamicParameters _params = new DynamicParameters();
_params.Add("#newId", DbType.Int32, direction: ParameterDirection.Output);
var result = connection.Execute("[dbo].YourProc", _params, null, null, CommandType.StoredProcedure);
var retVal = _params.Get<int>("newId");
DynamicParameters _params = new DynamicParameters(new {
ID = 123,
Year = 2020,
OutputParameter = 0
});
_params.Add("OutputParameter", 0, direction: ParameterDirection.Output);
var result = connection.Execute("[dbo].YourProc", _params, commandType: CommandType.StoredProcedure);
var retVal = _params.Get<int>("OutputParameter");
Here's what I've used to create a generic method for calling stored procedures with generic output parameters.
public void CallProcedure<I, O>(
string storedProcedure,
I inputParameters,
O outputParameters,
string connectionId = "Default")
{
using IDbConnection connection =
new SqlConnection(_config.GetConnectionString(connectionId));
var dynamicParameters = new DynamicParameters();
if (inputParameters != null)
dynamicParameters.AddDynamicParams(inputParameters);
if (outputParameters != null)
foreach (PropertyInfo prop in outputParameters.GetType().GetProperties())
dynamicParameters.Add(prop.Name,
prop.GetValue(outputParameters),
GetDbType(prop.PropertyType),
ParameterDirection.Output);
connection.ExecuteAsync(storedProcedure, dynamicParameters,
commandType: CommandType.StoredProcedure);
if (outputParameters != null)
foreach (PropertyInfo prop in outputParameters.GetType().GetProperties())
{
var method = typeof(DynamicParameters)
.GetMethod("Get")
.MakeGenericMethod(new Type[] { prop.PropertyType });
var outputParamValue = method.Invoke(dynamicParameters, new object[] { prop.Name });
prop.SetValue(outputParameters, outputParamValue);
}
}
What you are looking for is the last few lines, below the second if (outputParameters != null)
I do not know how fast it is, as this code is a part of a sketch project.
I also asked this question 6 years later, so you can check if anyone answered it here: Get all stored procedure output parameters using generic type in dapper
All in all this is a good headsup for dapper team to create a simple method we can call to get all output parameters of a stored procedure, instead of us getting into reflection.

How to increment ID before any insert with NHibernate

It looks like NH gets MAX(ID) only once, at first insert and then stores this value internally, this causes me some problems when other processes inserts data. Then I have not actual ID and duplicate key exception is thrown.
Lets imagine we have table Cats
CREATE TABLE Cats(ID int, Name varchar(25))
Then we have corresponding mapping done with FluentNhibernate
public class CatMap : ClassMap<Cat>
{
public CatMap()
{
Id(m=>m.ID).GeneratedBy.Increment();
Map(m=>.Name);
}
}
All I want to achieve is to insert my Cat records with ID's generated by NHibernate using SELECT MAX(ID) FROM Cats before any insert. Executing Session.Flush after any commit dosnt work. I'v done some investigation using SQL Server profiler, and this sql stetement is executed only once (at first insert) - other inserts doesnt force to retreive actual MAX(ID). I know that other algorithms like HiLo are better, but I cant replace it.
As you found out, the NHibernate Increment id generator was not intended for use in a multi-user environment. You state that using a HiLo generator is not an option so you're left with these options:
use the Native generator and change the id column to use the database supported identity mechanism
use the Assigned generator and write code to determine the next valid id
create a Custom generator where you implement the IIdentifierGenerator interface to do what you need
Below is sample code for a custom generator that uses a generalized proc to get an ID for a given table. The main issue with this approach is that you must wrap the code in something like a Unit of Work pattern to ensure the 'select max(id) ..." and the insert are covered by the same database transaction. The IIdentifierGenerator link has the XML mapping you need to wire up this custom generator.
using System;
using System.Collections.Generic;
using System.Data;
using NHibernate.Dialect;
using NHibernate.Engine;
using NHibernate.Id;
using NHibernate.Persister.Entity;
using NHibernate.Type;
namespace YourCompany.Stuff
{
public class IdGenerator : IIdentifierGenerator, IConfigurable
{
private string _tableName;
// The "select max(id) ..." query will go into this proc:
private const string DefaultProcedureName = "dbo.getId";
public string ProcedureName { get; protected set; }
public string TableNameParameter { get; protected set; }
public string OutputParameter { get; protected set; }
public IdGenerator()
{
ProcedureName = DefaultProcedureName;
TableNameParameter = "#tableName";
OutputParameter = "#newID";
}
public object Generate(ISessionImplementor session, object obj)
{
int newId;
using (var command = session.Connection.CreateCommand())
{
var tableName = GetTableName(session, obj.GetType());
command.CommandType = CommandType.StoredProcedure;
command.CommandText = ProcedureName;
// Set input parameters
var parm = command.CreateParameter();
parm.Value = tableName;
parm.ParameterName = TableNameParameter;
parm.DbType = DbType.String;
command.Parameters.Add(parm);
// Set output parameter
var outputParameter = command.CreateParameter();
outputParameter.Direction = ParameterDirection.Output;
outputParameter.ParameterName = OutputParameter;
outputParameter.DbType = DbType.Int32;
command.Parameters.Add(outputParameter);
// Execute the stored procedure
command.ExecuteNonQuery();
var id = (IDbDataParameter)command.Parameters[OutputParameter];
newId = int.Parse(id.Value.ToString());
if (newId < 1)
throw new InvalidOperationException(
string.Format("Could not retrieve a new ID with proc {0} for table {1}",
ProcedureName,
tableName));
}
return newId;
}
public void Configure(IType type, IDictionary<string, string> parms, Dialect dialect)
{
_tableName = parms["TableName"];
}
private string GetTableName(ISessionImplementor session, Type objectType)
{
if (string.IsNullOrEmpty(_tableName))
{
//Not set by configuration, default to the mapped table of the actual type from runtime object:
var persister = (IJoinable)session.Factory.GetClassMetadata(objectType);
var qualifiedTableName = persister.TableName.Split('.');
_tableName = qualifiedTableName[qualifiedTableName.GetUpperBound(0)]; //Get last string
}
return _tableName;
}
}
}