I have this class:
public class Parameters
{
public string UserId {get;set;}
public string OrgId {get;set;}
public string Roles {get;set;}
}
It gets deserialised from a JSON string. So some of the properties are null.
What are the best ways to build up the params list to pass to Dapper.
At the moment my logic for building up the params string to tag on the end of the SQL statement goes like this :
var parameters = string.Empty;
var parametersObj = new { };
if (query.Parameters != null)
{
if (!string.IsNullOrWhiteSpace(query.Parameters.UserId))
{
parameters = string.Format("{0} UserId = #UserId", parameters);
// parametersObj.UserId =
}
if (!string.IsNullOrWhiteSpace(query.Parameters.OrganisationIdentifier))
{
parameters = string.Format("{0}, OrganisationIdentifier = #OrganisationIdentifier", parameters);
}
if (!string.IsNullOrWhiteSpace(query.Parameters.Roles))
{
parameters = string.Format("{0}, Roles = #Roles", parameters);
}
}
var sqlString = string.Format("exec {0} {1}", query.DbObjectName, parameters);
conn.QueryAsync<dynamic>(sqlString, )
As you can see with the parametersObj I was going with the JavaScript way of dynamically building an object. If I did do this with dynamic instead of an object - will it still work?
example:
var parameters = string.Empty;
dynamic parametersObj = new { };
if (query.Parameters != null)
{
if (!string.IsNullOrWhiteSpace(query.Parameters.UserId))
{
parameters = string.Format("{0} UserId = #UserId", parameters);
parametersObj.UserId = query.Parameters.UserId;
}
if (!string.IsNullOrWhiteSpace(query.Parameters.OrganisationIdentifier))
{
parameters = string.Format("{0} OrganisationIdentifier = #OrganisationIdentifier ", parameters);
parametersObj.OrganisationIdentifier= query.Parameters.OrganisationIdentifier;
}
if (!string.IsNullOrWhiteSpace(query.Parameters.Roles))
{
parameters = string.Format("{0} Roles = #Roles", parameters);
parametersObj.Roles= query.Parameters.Roles;
}
}
var sqlString = string.Format("exec {0} {1}", query.DbObjectName, parameters);
conn.QueryAsync<dynamic>(sqlString, parametersObj);
I think the second example will work when you change
dynamic parametersObj = new {};
to
dynamic parametersObj = new ExpandoObject();
and the query to
conn.QueryAsync(sqlString, new
{
UserId = parametersObj.UserId,
...
};
NOTE: filling in the dynamic object like
conn.QueryAsync(sqlString, parametersObj);
will raise the error
Extension methods cannot be dynamically dispatched
You don't need to do anything: just pass your object as the parameters. Dapper will only pass in properties/parameters that it can identify in the query... and even if it passed them all in: it understands null.
The object is fine.
...QueryAsync(sql, query.Parameters)...
Related
I have such a problem that I have the same Func in my methods. I took it out separately, but now the problem is how to pass parameters to it. I want to use it in several places, but I don't know how to get the necessary parameters from the QueryAsync method
My func delegate
private Func<Authors, AuthorInPrintingEditions, PrintingEditions, Authors> _relationDelegate =
(Authors authors, AuthorInPrintingEditions relation, PrintingEditions printingEdition) =>
{
if (!_dictionaryResult.ContainsKey(authors.Id))
{
_dictionaryResult.Add(authors.Id, new Authors()
{
Id = authors.Id,
Name = authors.Name,
AuthorInPrintingEditions = printingEdition == null
? new List<AuthorInPrintingEditions>()
: new List<AuthorInPrintingEditions>()
{
new AuthorInPrintingEditions()
{
PrintingEdition = printingEdition
}
}
});
}
else
{
if (printingEdition != null)
{
_dictionaryResult[authors.Id].AuthorInPrintingEditions.Add(new AuthorInPrintingEditions()
{
PrintingEdition = printingEdition
});
}
}
return authors;
};
My method, when I want use it
using (var conneсtion = CreateConnection())
{
await conneсtion.QueryAsync<Authors, AuthorInPrintingEditions, PrintingEditions, Authors>(query,
(authors, authorInPrintingEditions, printingEdition), <--- in this row I want use delegate
new { skip = skip, pageSize = pageSize }, splitOn: "Id,AuthorId,Id");
return _dictionaryResult.Values.ToList();
}
I don't understand how to pass parameters to it and whether it is generally possible, or it will still be easier to copy and paste
If you wish to reuse the delegate and also be able to use parameters,
try using something like this:
public class AuthorsMapper
{
public int Parameter1;
public string Parameter2;
public Func<Authors, AuthorInPrintingEditions, PrintingEditions, Authors> Map =>
// Your existing code that can now use parameters
}
And use it like this:
var mapper = new AuthorsMapper
{
Parameter1 = 742,
Parameter2 = "House"
};
await conneсtion.QueryAsync<Authors, AuthorInPrintingEditions, PrintingEditions, Authors>
(query, mapper.Map, ...);
I have a query
string sQuery = string.Format("SELECT {0} FROM vwweb_Orders WHERE CustFID = ?", columns);
that gets executed here
var result = await conn.QueryAsync<Order>(sQuery, new { ID = Custid });
But say that I'm searching based on parameters chosen by the user. I can build up the where clause, but how do I build up the anonymous array?
new { ID = Custid }
I want something like
var params = new {};
if (!String.IsNullOrWhiteSpace(username)) {
params += {username}
}
If you really want to have params like an anonymous type, you can use an ExpandoObject:-
dynamic params = new ExpandoObject();
if (!string.IsNullOrWhiteSpace(username)) {
params.Username = username;
}
Or if you want an array (and you don't know the length ahead of time), use a List<string>:-
var paramlist = new List<string>();
if (!string.IsNullOrWhiteSpace(username)) {
paramlist.Add("username");
}
var params = paramlist.ToArray();
However, if you are constructing the WHERE clause, you will always have a fixed number of parameters in your SQL statement anyway (or you'll have to construct it dynamically too).
One other method you can use when dynamically building a query for filtering is this:-
SELECT *
FROM vwweb_Orders
WHERE 1=1
AND (#custid IS NULL OR CustFID = #custid)
AND (#xyz IS NULL OR XYZ = #xyz)
-- etc
Then supply all the parameters to your QueryAsync call, and if any are null, they'll be skipped in the WHERE clause.
Maybe you can write your query to check for null/empty/non-zero values first otherwise evaluate the real values as follows:
public async Task<List<Order>> Execute(OrderQuery query)
{
var sql = $#"SELECT
...
FROM vwweb_Orders
WHERE #{nameof(query.CustomerId)} <= 0 OR customer_id = #{nameof(query.CustomerId)}
AND ISNULL(#{nameof(query.CustomerName)}, '') = '' OR customer_name = #{nameof(query.CustomerName)}";
return await conn.QueryAsync<Order>(sql, new { query.CustomerId, query.CustomerName});
}
public class OrderQuery
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
}
public class Order
{
}
I have this class:
public class allFields
{
public string EAN { get; set; }
public string title { get; set; }
public string qty { get; set; }
public string price { get; set; }
public DateTime date { get; set; }
}
And a function that return an anonymous type:
public IEnumerable<object> stockEtatQty()
{
List<allFields> afList = new List<allFields>();
var query = from x in ctx.book
where x.qty > 0
select x;
foreach (var item in query)
{
allFields af = new allFields();
af.EAN = item.EAN;
af.title = item.Titre;
af.qty = ""+item.Quantite;
afList.Add(af);
}
var q = from x in afList
select new { EAN=x.EAN, Title=x.title, Quantity=x.qty };
return q; //q is a IEnumerable<'a> where a is new {string EAN, string Title, string Quantity}
}
In my WinForm a use this function as below:
private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
ServiceStock sstock = new ServiceStock();
var q = sstock.stockEtatQty().ToList();// q is a list<object>
string str = "";
foreach (var item in q)
{
str += item + Environment.NewLine;
}
MessageBox.Show(str);
}
The result is:
{ EAN = 1, Title = CSharp Security, Quantity = 970 }
{ EAN = 2, Title = MISC, Quantity = 100 }
...
What I want?
I want not like the result above, but separate each field apart of the item in the loop foreach, e.g get item.EAN, item.Title and item.Quantity.
If there is no solution for my problem I would like to know an alternative,
Thanks for help.
The obvious solution is to create a custom type (let's call it BookInfo) and return a IEnumerable<BookInfo> instead of a IEnumerable<object> (and maybe override ToString if you want to put the formatting into this class itself).
Then you can easily format the output.
public class BookInfo
{
public string EAN {get;set;}
public string Title {get;set;}
public int Quantity {get;set;}
}
public IEnumerable<BookInfo> stockEtatQty()
{
...
var q = from x in afList
select new BookInfo { EAN=x.EAN, Title=x.title, Quantity=x.qty };
return q;
}
private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
ServiceStock sstock = new ServiceStock();
var q = sstock.stockEtatQty();
var message = string.Join(Environment.NewLine,
q.Select(item => String.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)));
MessageBox.Show(message);
}
Since the static type information about the object of anonymous type is lost by the time that you exit stockEtatQty() method, you could cast the object to dynamic and access fields like this:
str = string.Join(Environment.NewLine, q.Cast<dynamic>().Select(item =>
string.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)
));
The cast to dynamic tells the compiler that EAN, Title, and Quantity need to be resolved at runtime.
Note that I also replaced the foreach loop with a call to string.Join to improve performance: repeated string concatenation creates unnecessary partial string objects, which string.Join avoids. Another solution would be to use StringBuider instead of string concatenation +=.
stockEtatQty is in a project (Service) and QuantityToolStripMenuItem_Click is in another project (View)
Unfortunately, this means that you would not be able to use anonymous types: anonymous types are generated with internal visibility, limiting their use to the assembly in which they are produced. You can use a work-around based on ExpandoObject described in this answer:
var q = afList.Select(x => {
dynamic res = new ExpandoObject();
res.EAN=x.EAN;
res.Title=x.title;
res.Quantity=x.qty;
return res;
});
Create a new class that represents the new object structure and return that.
var q = from x in afList
select new SmallerType { EAN=x.EAN, Title=x.title, Quantity=x.qty };
WinForm Function
foreach (SmallerType item in q)
{
//
}
You can use collection of dynamic objects instead of simple objects as return type of your method:
public IEnumerable<dynamic> stockEtatQty()
Then you will not have IntelliSense but at runtime properties will be found:
foreach (var item in sstock.stockEtatQty())
str += String.Format("{0}", item.EAN) + Environment.NewLine;
But I suggest you to create custom class with EAN, Title and Quantity properties. Or just use your allFields instead of anonymous objects.
Consider also to use StringBuilder for string creation to avoid creating lot of in-memory strings:
var builder = new StringBuilder();
foreach (var item in sstock.stockEtatQty())
builder.AppendFormat("{0}{1}", item.EAN, Environment.NewLine);
MessageBox.Show(builder.ToString());
Sorry if the title does not reflect what I actually want.
I'm creating a generic class for selecting, updating, inserting and deleting dates from and to a database.
Basically, I want a function that gives me back an ObservableCollection<"can be anything"> ==> Where anything is a class and not strings. I would like to know if it is possible to do this, if yes, please,help me how I can achieve this.
this is my starting point:
//class a
public static ObservableCollection<ContactPerson> contactPersons = new ObservableCollection<ContactPerson>();
public static ObservableCollection<ContactPerson> getContactPerson()
{
contactPersons = (ObservableCollection<ContactPerson>)DBConnection.GetDataOutDatabase(typeof(ContactPerson), "Contactpersoon");
return contactPersons;
}
//class b
public static Object GetDataOutDatabase(Type myType,String table)
{
ObservableCollection<Object> objecten = new ObservableCollection<Object>();
string sql = "SELECT * FROM " + table;
DbDataReader reader = Database.GetData(sql);
while (reader.Read())
{
objecten.Add(Create(myType, reader));
}
return objecten;
}
private static Object Create(Type myType, IDataRecord record)
{
PropertyInfo[] myPropertyInfo = myType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < myPropertyInfo.Length; i++)
{
PropertyInfo myPropInfo = (PropertyInfo)myPropertyInfo[i];
String name = myPropInfo.Name;
Type type = myPropInfo.PropertyType;
}
return null;
}
And this is what I ultimately want to get. Is this possible?
//ContactPerson cp = new ContactPerson();
//cp.ID = (record["ID"].ToString());
//cp.Name = record["Name"].ToString();
//cp.Company = record["Company"].ToString();
//cp.JobTitle = new ContactPersonTitle()
//{
// Name = record["JobTitle"].ToString(),
//};
//cp.JobRole = new ContactPersonType()
//{
// Name = record["JobRole"].ToString(),
//};
//cp.City = record["City"].ToString();
//cp.Email = record["Email"].ToString();
//cp.Phone = record["Phone"].ToString();
//cp.Cellphone = record["Cellphone"].ToString();
Many thanks!
You can actually do this with reflection in generic methods.
public class DBConnection
{
public static ObservableCollection<T> GetDataOutDatabase<T>(string table)
{
var objecten = new ObservableCollection<T>();
string sql = "SELECT * FROM " + table;
DbDataReader reader = Database.GetData(sql);
while (reader.Read())
{
objecten.Add(Create<T>(reader));
}
return objecten;
}
public static T Create<T>(IDataRecord record)
{
var properties = typeof(T).GetProperties();
var returnVal = Activator.CreateInstance(typeof(T));
properties.ToList().ForEach(item =>
{
try
{
if (item.PropertyType.IsPrimitive)
{
item.SetValue(returnVal, Convert.ChangeType(record[item.Name].ToString(), item.PropertyType),null);
}
else
{
object[] parameters = {record};
var value =
typeof(DBConnection).GetMethod("Create").MakeGenericMethod(item.PropertyType).Invoke(null, parameters);
item.SetValue(returnVal,value,null);
}
}
catch
{
Write("Property Not Found");
}
});
return (T)returnVal;
}
}
The example above does assume that all properties names match the column names you are retrieving from your database communication. For instance in the ContactPersonTitle above rather than Name you would need to have JobTitle as the property name.
Not as you are currently doing it. You should look into the entity framework which allows translation of database tables datacollections.
have a look at:
http://www.codeproject.com/Articles/363040/An-Introduction-to-Entity-Framework-for-Absolute-B
I have very less experiece with Generics and Reflection. What I assumed so for from the following sample is that it takes too much time to perform. Is there a way so that i accomplish the following without using reflection..
SCENARIO
I am working on a method which is generic. it takes an instance of a class passed to it and make SqlParameters from all of the properties. following is the code for generic method called "Store", and one more method that converts c# type to SqlDbType of DbType.
List<SqlParameter> parameters = new List<SqlParameter>();
public T Store<T>(T t)
{
Type type = t.GetType();
PropertyInfo[] props = (t.GetType()).GetProperties();
foreach (PropertyInfo p in props)
{
SqlParameter param = new SqlParameter();
Type propType = p.PropertyType;
if (propType.BaseType.Name.Equals("ValueType") || propType.BaseType.Name.Equals("Array"))
{
param.SqlDbType = GetDBType(propType); //e.g. public bool enabled{get;set;} OR public byte[] img{get;set;}
}
else if (propType.BaseType.Name.Equals("Object"))
{
if (propType.Name.Equals("String"))// for string values
param.SqlDbType = GetDBType(propType);
else
{
dynamic d = p.GetValue(t, null); // for referrences e.g. public ClassA obj{get;set;}
Store<dynamic>(d);
}
}
param.ParameterName = p.Name;
parameters.Add(param);
}
return t;
}
// mehthod for getting the DbType OR SqlDbType from the type...
private SqlDbType GetDBType(System.Type type)
{
SqlParameter param;
System.ComponentModel.TypeConverter tc;
param = new SqlParameter();
tc = System.ComponentModel.TypeDescriptor.GetConverter(param.DbType);
if (tc.CanConvertFrom(type))
{
param.DbType = (DbType)tc.ConvertFrom(type.Name);
}
else
{
// try to forcefully convert
try
{
param.DbType = (DbType)tc.ConvertFrom(type.Name);
}
catch (Exception e)
{
switch (type.Name)
{
case "Char":
param.SqlDbType = SqlDbType.Char;
break;
case "SByte":
param.SqlDbType = SqlDbType.SmallInt;
break;
case "UInt16":
param.SqlDbType = SqlDbType.SmallInt;
break;
case "UInt32":
param.SqlDbType = SqlDbType.Int;
break;
case "UInt64":
param.SqlDbType = SqlDbType.Decimal;
break;
case "Byte[]":
param.SqlDbType = SqlDbType.Binary;
break;
}
}
}
return param.SqlDbType;
}
To call my method suppose i have 2 classes as following
public class clsParent
{
public int pID { get; set; }
public byte[] pImage { get; set; }
public string pName { get; set; }
}
and
public class clsChild
{
public decimal childId { get; set; }
public string childName { get; set; }
public clsParent parent { get; set; }
}
and this is a call
clsParent p = new clsParent();
p.pID = 101;
p.pImage = new byte[1000];
p.pName = "John";
clsChild c = new clsChild();
c.childId = 1;
c.childName = "a";
c.parent = p;
Store<clsChild>(c);
If you want to get rid of reflection, you may find inspiration in the code below.
Here all access to objects to store in database as well as the sql property value assignment is handled by a runtime compiled expression build from the data type.
The table holding the values is assumed to be test and the field names are assumed to be identical to the property values.
For each property a Mapping<T> is constructed. It will hold a FieldName containing the database field, a SqlParameter which is supposed to be inserted correctly into a SQL INSERT statement (example in main) and finally if contains the compiled action, that can take an instance of the input T object and assign the value to the SqlParameters property Value. Construction of a collection of these mappings are done in the Mapper<T> class. Code is inlined for explanation.
Finally the main method shows how to bind the stuff together.
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
namespace ExpTest
{
class Program
{
public class Mapping<T>
{
public Mapping(string fieldname, SqlParameter sqlParameter, Action<T, SqlParameter> assigner)
{
FieldName = fieldname;
SqlParameter = sqlParameter;
SqlParameterAssignment = assigner;
}
public string FieldName { get; private set; }
public SqlParameter SqlParameter { get; private set; }
public Action<T, SqlParameter> SqlParameterAssignment { get; private set; }
}
public class Mapper<T>
{
public IEnumerable<Mapping<T>> GetMappingElements()
{
foreach (var reflectionProperty in typeof(T).GetProperties())
{
// Input parameters to the created assignment action
var accessor = Expression.Parameter(typeof(T), "input");
var sqlParmAccessor = Expression.Parameter(typeof(SqlParameter), "sqlParm");
// Access the property (compiled later, but use reflection to locate property)
var property = Expression.Property(accessor, reflectionProperty);
// Cast the property to ensure it is assignable to SqlProperty.Value
// Should contain branching for DBNull.Value when property == null
var castPropertyToObject = Expression.Convert(property, typeof(object));
// The sql parameter
var sqlParm = new SqlParameter(reflectionProperty.Name, null);
// input parameter for assignment action
var sqlValueProp = Expression.Property(sqlParmAccessor, "Value");
// Expression assigning the retrieved property from input object
// to the sql parameters 'Value' property
var dbnull = Expression.Constant(DBNull.Value);
var coalesce = Expression.Coalesce(castPropertyToObject, dbnull);
var assign = Expression.Assign(sqlValueProp, coalesce);
// Compile into action (removes reflection and makes real CLR object)
var assigner = Expression.Lambda<Action<T, SqlParameter>>(assign, accessor, sqlParmAccessor).Compile();
yield return
new Mapping<T>(reflectionProperty.Name, // Table name
sqlParm, // The constructed sql parameter
assigner); // The action assigning from the input <T>
}
}
}
public static void Main(string[] args)
{
var sqlStuff = (new Mapper<Data>().GetMappingElements()).ToList();
var sqlFieldsList = string.Join(", ", sqlStuff.Select(x => x.FieldName));
var sqlValuesList = string.Join(", ", sqlStuff.Select(x => '#' + x.SqlParameter.ParameterName));
var sqlStmt = string.Format("INSERT INTO test ({0}) VALUES ({1})", sqlFieldsList, sqlValuesList);
var dataObjects = Enumerable.Range(1, 100).Select(id => new Data { Foo = 1.0 / id, ID = id, Title = null });
var sw = Stopwatch.StartNew();
using (SqlConnection cnn = new SqlConnection(#"server=.\sqlexpress;database=test;integrated security=SSPI"))
{
cnn.Open();
SqlCommand cmd = new SqlCommand(sqlStmt, cnn);
cmd.Parameters.AddRange(sqlStuff.Select(x => x.SqlParameter).ToArray());
dataObjects.ToList()
.ForEach(dto =>
{
sqlStuff.ForEach(x => x.SqlParameterAssignment(dto, x.SqlParameter));
cmd.ExecuteNonQuery();
});
}
Console.WriteLine("Done in: " + sw.Elapsed);
}
}
public class Data
{
public string Title { get; set; }
public int ID { get; set; }
public double Foo { get; set; }
}
}
I think you would in general benefit from using a standard ORM like NHibernate or Entity Framework. Both can do (customizable) mappings from classes to relational databases and NHibernate gives you full flexibility between all standard DBMS systems.
Having said that, you should be able to obtain some of the functionality by using Linq expressions, that can later be compiled; this should give you better performance.
Someone told you that reflection is really performance heavy, but you haven't really run your code through a profiler.
I tried your code, and it took 18 ms (65000 ticks) to run, I must say that it is fairly fast compared to the time it will take to save the data in the database.
But you are right about that it is really too much time.
I found that your code raised one Exception when it called tc.ConvertFrom when converting Byte[].
Removing the byte[] pImage from clsParent the runtime dropped to 850 ticks.
The performance problem here was the Exception, not the reflection.
I have taken the liberty to change your GetDBType to this:
private SqlDbType GetDBType(System.Type type)
{
SqlParameter param;
System.ComponentModel.TypeConverter tc;
param = new SqlParameter();
tc = System.ComponentModel.TypeDescriptor.GetConverter(param.DbType);
if (tc.CanConvertFrom(type))
{
param.DbType = (DbType)tc.ConvertFrom(type.Name);
}
else
{
switch (type.Name)
{
case "Char":
param.SqlDbType = SqlDbType.Char;
break;
case "SByte":
param.SqlDbType = SqlDbType.SmallInt;
break;
case "UInt16":
param.SqlDbType = SqlDbType.SmallInt;
break;
case "UInt32":
param.SqlDbType = SqlDbType.Int;
break;
case "UInt64":
param.SqlDbType = SqlDbType.Decimal;
break;
case "Byte[]":
param.SqlDbType = SqlDbType.Binary;
break;
default:
try
{
param.DbType = (DbType)tc.ConvertFrom(type.Name);
}
catch
{
// Some error handling
}
break;
}
}
return param.SqlDbType;
}
I hope this will help you in your quest.
Not an alternative, but only a suggestion: If the types are repeatedly stored during runtime you can try to tweak the reflection approach by introducing some caching.
Instead of having:
PropertyInfo[] props = (t.GetType()).GetProperties();
try following caching approach:
PropertyInfo[] props = GetProperties(type);
where GetProperties(Type) is a implemented like this:
private Dictionary<Type, PropertyInfo[]> propertyCache;
// ...
public PropertyInfo[] GetProperties(Type t)
{
if (propertyCache.ContainsKey(t))
{
return propertyCache[t];
}
else
{
var propertyInfos = t.GetProperties();
propertyCache[t] = propertyInfos;
return propertyInfos;
}
}
This is how you can cache the Type.GetProperties() method call. You can apply the same approach which such lookups for some other parts of the code. For instance the place where you use param.DbType = (DbType)tc.ConvertFrom(type.Name);. It is also possible to replace ifs and switchs with lookups. But before you do something like this you should really do some profiling. It complicates code a lot and you should not do that without a good reason.
There is no alternative to reflection unless using post-buil treatment to rewrite code.
You can use reflection without any performance issue if you use it only to prepare dynamic emitted type/method/delegate and include it naturally as a strategy pattern.