I have the following code to use the Where and And filter.
public void Add(Expression<Func<T, object>> memberExpression, object memberValue)
{
if (_Query.ToString() != string.Empty)
_Query.Append(" AND ");
_Query.Append(string.Format(" [{0}] = {1}", NameOf(memberExpression), memberValue == null ? "NULL" : string.Format("'{0}'", memberValue)));
}
I took this same method and made some changes to use Like in sqlite but only works with the first letter entered if entering 2 letters no longer brings any value
Code Like:
public void Like(Expression<Func<T, object>> memberExpression, object memberValue)
{
if (_Query.ToString() != string.Empty)
_Query.Append(" AND ");
_Query.Append(string.Format(" [{0}] LIKE '{1}' ", NameOf(memberExpression), memberValue == null ? "NULL" : "%" + memberValue + "%"));
}
With a letter
With more than 1 letter
See that you should bring the record "William"
For anyone who is talking about sql injection I simply got this Sqlite example and I'm doing a little crud.
EXAMPLE CRUD
Edit:
sql to run on "Db browser for Sqlite"
Class
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace SQLiteWithCSharp.Utility
{
/// <summary>
/// Author : Swaraj Ketan Santra
/// Email : swaraj.ece.jgec#gmail.com
/// Date : 25/02/2017
/// Description : Entity/model classes as T
/// </summary>
/// <typeparam name="T"> Class and newable type</typeparam>
#region Attributes
/// <summary>
/// Use this attribute to decorate the properties on your model class.
/// Only those properties that are having exactly the same column name of a DB table.
/// </summary>
public class DbColumnAttribute : Attribute
{
/// <summary>
/// Set true if implicit conversion is required.
/// </summary>
public bool Convert { get; set; }
/// <summary>
/// Set true if the property is primary key in the table
/// </summary>
public bool IsPrimary { get; set; }
/// <summary>
/// Denotes if the field is an identity type or not.
/// </summary>
public bool IsIdentity { get; set; }
}
#endregion
#region BaseService
/// <summary>
/// Use with Entity/Model class only
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class BaseService<T> where T : class, new()
{
#region Constructor
public BaseService()
{
}
/// <summary>
/// Pass the connection string in constructor
/// </summary>
/// <param name="connectionString"></param>
public BaseService(string connectionString)
{
Context.ConnectionString = connectionString;
}
#endregion
#region Public methods
/// <summary>
/// To get SQLite connection object
/// </summary>
/// <returns>SQLiteConnection object</returns>
public SQLiteConnection GetConnection()
{
return new SQLiteConnection(Context.ConnectionString);
}
/// <summary>
/// Inserts the single record into table
/// </summary>
/// <param name="entity"></param>
public long Add(T entity)
{
long identity = 0;
bool hasIdentity = false;
StringBuilder columns = new StringBuilder();
StringBuilder values = new StringBuilder();
IList<PropertyInfo> propertyInfos = GetPropertyInfoList(entity);
foreach (PropertyInfo i in propertyInfos)
{
var ca = i.GetCustomAttribute(typeof(DbColumnAttribute)) as DbColumnAttribute;
if (ca != null)
{
if (!ca.IsIdentity)
{
columns.Append(string.Format("[{0}],", i.Name));
values.Append(string.Format("{0},",
i.GetValue(entity) == null ? "NULL" : string.Format("'{0}'", i.GetValue(entity))));
}
else
{
hasIdentity = true;
}
}
}
if (columns.ToString() != string.Empty)
{
columns.Remove(columns.Length - 1, 1); // Remove additional comma(',')
values.Remove(values.Length - 1, 1); // Remove additional comma(',')
StringBuilder qry = new StringBuilder();
qry.Append(string.Format("INSERT INTO [{0}] ( {1} ) VALUES ( {2} ); SELECT last_insert_rowid();"
, entity.GetType().Name, columns, values));
identity = hasIdentity ? Execute(qry.ToString(), true) : Execute(qry.ToString());
}
return identity;
}
/// <summary>
/// Inserts multiple records into a table
/// </summary>
/// <param name="entities"></param>
public void AddRange(IList<T> entities)
{
StringBuilder qry = new StringBuilder();
foreach (T entity in entities)
{
StringBuilder columns = new StringBuilder();
StringBuilder values = new StringBuilder();
IList<PropertyInfo> propertyInfos = GetPropertyInfoList(entity);
foreach (PropertyInfo i in propertyInfos)
{
var ca = i.GetCustomAttribute(typeof(DbColumnAttribute)) as DbColumnAttribute;
if (ca != null)
{
if (!ca.IsIdentity)
{
columns.Append(string.Format("[{0}],", i.Name));
values.Append(string.Format("{0},",
i.GetValue(entity) == null ? "NULL" : string.Format("'{0}'", i.GetValue(entity))));
}
}
}
if (columns.ToString() != string.Empty)
{
columns.Remove(columns.Length - 1, 1); // Remove additional comma(',')
values.Remove(values.Length - 1, 1); // Remove additional comma(',')
qry.AppendLine(string.Format("INSERT INTO [{0}] ( {1} ) VALUES ( {2} );"
, entity.GetType().Name, columns, values));
}
}
try
{
Execute(qry.ToString());
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// Updates single entity
/// </summary>
/// <param name="entity"></param>
public void Update(T entity)
{
StringBuilder columns = new StringBuilder();
StringBuilder clause = new StringBuilder();
IList<PropertyInfo> propertyInfos = GetPropertyInfoList(entity);
foreach (PropertyInfo i in propertyInfos)
{
var ca = i.GetCustomAttribute(typeof(DbColumnAttribute)) as DbColumnAttribute;
if (ca != null)
{
if (!ca.IsPrimary)
{
columns.Append(string.Format("[{0}] = {1},", i.Name,
i.GetValue(entity) == null ? "NULL" : string.Format("'{0}'", i.GetValue(entity))));
}
else
{
clause.Append(string.Format("[{0}] = '{1}'", i.Name, i.GetValue(entity)));
}
}
}
if (columns.ToString() != string.Empty)
{
columns.Remove(columns.Length - 1, 1);
StringBuilder qry = new StringBuilder();
qry.Append(string.Format("UPDATE [{0}] SET {1} WHERE {2};"
, entity.GetType().Name, columns, clause));
Execute(qry.ToString());
}
}
/// <summary>
/// Updates mutiple entities in single query
/// </summary>
/// <param name="entities"></param>
public void UpdateRange(IList<T> entities)
{
StringBuilder qry = new StringBuilder();
foreach (T entity in entities)
{
StringBuilder columns = new StringBuilder();
StringBuilder clause = new StringBuilder();
#region MyRegion
IList<PropertyInfo> propertyInfos = GetPropertyInfoList(entity);
foreach (PropertyInfo i in propertyInfos)
{
var ca = i.GetCustomAttribute(typeof(DbColumnAttribute)) as DbColumnAttribute;
if (ca != null)
{
if (!ca.IsPrimary)
{
columns.Append(string.Format("[{0}] = {1},", i.Name,
i.GetValue(entity) == null ? "NULL" : string.Format("'{0}'", i.GetValue(entity))));
}
else
{
clause.Append(string.Format("[{0}] = '{1}'", i.Name, i.GetValue(entity)));
}
}
}
if (columns.ToString() != string.Empty)
{
columns.Remove(columns.Length - 1, 1);
qry.AppendLine(string.Format("UPDATE [{0}] SET {1} WHERE {2};"
, entity.GetType().Name, columns, clause));
}
#endregion
}
Execute(qry.ToString());
}
/// <summary>
/// Find single item
/// </summary>
/// <param name="cmdText"></param>
public T GetById(object id)
{
T entity = new T();
StringBuilder clause = new StringBuilder();
IList<PropertyInfo> pInfos = GetPropertyInfoList(entity);
foreach (var pi in pInfos)
{
var pk = pi.GetCustomAttribute(typeof(DbColumnAttribute)) as DbColumnAttribute;
if (pk != null && pk.IsPrimary)
{
clause.Append(string.Format("[{0}]='{1}'", pi.Name, id));
break;
}
}
if (clause.ToString() != string.Empty)
{
StringBuilder qry = new StringBuilder();
qry.Append(string.Format("SELECT * FROM [{0}] WHERE {1}", entity.GetType().Name, clause));
var _entities = ExecuteGet(qry.ToString());
if (_entities != null && _entities.Count > 0)
entity = _entities[0];
}
return entity;
}
public IList<T> Find(IEnumerable<object> ids)
{
IList<T> entities = new List<T>();
StringBuilder clause = new StringBuilder();
var entity = new T();
IList<PropertyInfo> pInfos = GetPropertyInfoList(entity);
foreach (var pi in pInfos)
{
var pk = pi.GetCustomAttribute(typeof(DbColumnAttribute)) as DbColumnAttribute;
if (pk != null && pk.IsPrimary)
{
string _ids = string.Empty;
foreach (var id in ids)
{
if (_ids != string.Empty)
_ids = _ids + ",";
_ids = _ids + id.ToString();
}
clause.Append(string.Format("[{0}] IN ({1})", pi.Name, _ids));
break;
}
}
if (clause.ToString() != string.Empty)
{
StringBuilder qry = new StringBuilder();
qry.Append(string.Format("SELECT * FROM [{0}] WHERE {1}", entity.GetType().Name, clause));
entities = ExecuteGet(qry.ToString());
}
return entities;
}
public IList<T> Find(IFilter<T> filter)
{
T entity = new T();
return ExecuteGet<T>(filter);
}
/// <summary>
/// Get all records
/// </summary>
/// <param name="cmdText"></param>
public IList<T> GetAll()
{
T entity = new T();
return ExecuteGet(string.Format("SELECT * FROM [{0}]", entity.GetType().Name));
}
/// <summary>
/// Pass comman text to get values
/// </summary>
/// <param name="commandText"></param>
/// <returns></returns>
public IList<T> GetAll(string commandText)
{
return ExecuteGet(commandText);
}
public IList<TEntity> GetAll<TEntity>(string commandText)
where TEntity : class, new()
{
return ExecuteGet<TEntity>(commandText);
}
#endregion
#region Private methods
/// <summary>
/// Execute Only ; No return
/// </summary>
/// <param name="cmdText"></param>
//private void Execute(string cmdText)
//{
// using (var connection = GetConnection())
// {
// connection.Open();
// SQLiteCommand cmd = new SQLiteCommand(cmdText, connection);
// cmd.ExecuteNonQuery();
// }
//}
private long Execute(string cmdText, bool returnIdentity = false)
{
using (var connection = GetConnection())
{
connection.Open();
SQLiteCommand cmd = new SQLiteCommand(cmdText, connection);
if (returnIdentity)
{
return (long)cmd.ExecuteScalar();
}
else
{
cmd.ExecuteNonQuery();
return 0;
}
}
}
/// <summary>
/// Execute and get records. of the native type
/// </summary>
/// <param name="cmdText"></param>
/// <returns></returns>
private IList<T> ExecuteGet(string cmdText)
{
using (var connection = GetConnection())
{
connection.Open();
SQLiteCommand cmd = new SQLiteCommand(cmdText, connection);
using (var reader = cmd.ExecuteReader())
{
return new EntityMapper().Map<T>(reader);
}
}
}
/// <summary>
/// Get list of items by specifying the type
/// </summary>
/// <param name="cmdText"></param>
/// <returns></returns>
private IList<TEntity> ExecuteGet<TEntity>(string cmdText)
where TEntity : class, new()
{
using (var connection = GetConnection())
{
connection.Open();
SQLiteCommand cmd = new SQLiteCommand(cmdText, connection);
using (var reader = cmd.ExecuteReader())
{
return new EntityMapper().Map<TEntity>(reader);
}
}
}
/// <summary>
/// Pass filter to get records in entity format
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="filter"></param>
/// <returns></returns>
private IList<TEntity> ExecuteGet<TEntity>(IFilter<TEntity> filter)
where TEntity : class, new()
{
using (var connection = GetConnection())
{
connection.Open();
SQLiteCommand cmd = new SQLiteCommand(filter.Query, connection);
using (var reader = cmd.ExecuteReader())
{
return new EntityMapper().Map<TEntity>(reader);
}
}
}
/// <summary>
/// Pass SQLite reader to get the specified entity type
/// when you are reading dataset or multiple records
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
private IList<TEntity> ExecuteGet<TEntity>(SQLiteDataReader reader)
where TEntity : class, new()
{
return new EntityMapper().Map<TEntity>(reader);
}
private IList<PropertyInfo> GetPropertyInfoList(T entity)
{
return entity.GetType().GetProperties()
.Where(p => p.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(DbColumnAttribute)) != null).ToList();
}
private IList<PropertyInfo> GetPropertyInfoList<TEntity>(TEntity entity)
{
return entity.GetType().GetProperties()
.Where(p => p.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(DbColumnAttribute)) != null).ToList();
}
#endregion
}
#endregion
#region Cache/Storage
public static class Context
{
public static string ConnectionString { get; set; }
}
#endregion
#region Entity Mapper
public class EntityMapper
{
// Complete
public IList<T> Map<T>(SQLiteDataReader reader)
where T : class, new()
{
IList<T> collection = new List<T>();
while (reader.Read())
{
T obj = new T();
foreach (PropertyInfo i in obj.GetType().GetProperties()
.Where(p => p.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(DbColumnAttribute)) != null).ToList())
{
try
{
var ca = i.GetCustomAttribute(typeof(DbColumnAttribute));
if (ca != null)
{
if (((DbColumnAttribute)ca).Convert == true)
{
if (reader[i.Name] != DBNull.Value)
i.SetValue(obj, Convert.ChangeType(reader[i.Name], i.PropertyType));
}
else
{
if (reader[i.Name] != DBNull.Value)
i.SetValue(obj, reader[i.Name]);
}
}
}
catch (Exception ex)
{
#if DEBUG
Console.WriteLine(ex.Message);
Console.ReadLine();
#endif
#if !DEBUG
throw ex;
#endif
}
}
collection.Add(obj);
}
return collection;
}
}
#endregion
#region Interfaces
public interface IFilter<T> where T : class, new()
{
string EntityName { get; }
string Query { get; }
void Add(Expression<Func<T, object>> memberExpression, object memberValue);
}
#endregion
#region Filter
public class Filter<T> : IFilter<T> where T : class, new()
{
public Filter()
{
_Query = new StringBuilder();
EntityName = typeof(T).Name;
}
public void Add(Expression<Func<T, object>> memberExpression, object memberValue)
{
if (_Query.ToString() != string.Empty)
_Query.Append(" AND ");
_Query.Append(string.Format(" [{0}] = {1}", NameOf(memberExpression), memberValue == null ? "NULL" : string.Format("'{0}'", memberValue)));
}
public void Like(Expression<Func<T, object>> memberExpression, object memberValue)
{
if (_Query.ToString() != string.Empty)
_Query.Append(" AND ");
_Query.Append(string.Format(" [{0}] LIKE '{1}' ", NameOf(memberExpression), memberValue == null ? "NULL" : "%" + memberValue + "%"));
}
public string EntityName { get; private set; }
private readonly StringBuilder _Query;
/// <summary>
/// Returns SELECT statement with WHERE clause based on the expression passed; This is CommandText
/// </summary>
public string Query
{
get
{
return string.Format("SELECT * FROM [{0}] {1} {2};"
, EntityName
, _Query.ToString() == string.Empty ? string.Empty : "WHERE"
, _Query.ToString());
}
}
private string NameOf(Expression<Func<T, object>> exp)
{
MemberExpression body = exp.Body as MemberExpression;
if (body == null)
{
UnaryExpression ubody = (UnaryExpression)exp.Body;
body = ubody.Operand as MemberExpression;
}
return body.Member.Name;
}
}
#endregion
}
Class Employee
using SQLiteWithCSharp.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SQLiteWithCSharp.Models
{
public class Employee
{
[DbColumn(IsIdentity =true, IsPrimary =true)]
public long EmployeeId { get; set; }
[DbColumn]
public string Name { get; set; }
[DbColumn]
public string Surname { get; set; }
[DbColumn]
public string Date_Birth { get; set; }
[DbColumn]
public string Home_Address { get; set; }
[DbColumn]
public string City { get; set; }
[DbColumn]
public string Postcode { get; set; }
[DbColumn]
public string Telephone { get; set; }
[DbColumn]
public string Mobile { get; set; }
[DbColumn]
public string Email { get; set; }
}
}
Look at your video on 0:13 and 0:14. The scroll bar disappears partly at the top. The top part of your table is hidden behind the header.
Check if your Sqlite has case sensitive LIKE on.
PRAGMA case_sensitive_like=ON;
PRAGMA case_sensitive_like=OFF;
I modified the String.Format as shown below. You cannot enclose NULL in quotes. So I am composing single quotes also as part of replacement. This works fine for me in sqlite.
_Query.Append(string.Format(" [{0}] LIKE {1} "
, NameOf(memberExpression)
, memberValue == null ? "NULL" : "'%" + memberValue + "%'"));
Related
I am trying to make use of Oracle's continuous query notification, but in WPF MVVM with Dapper and Caliburn.Micro.
I've set up a static OracleConnector class where dapper is querying db and populates a List like this:
List<Employees> List = cnn.Query<Employees>(OracleDynamicParameters.sql, param: dyParam).AsList();
In my View I bound a DataGrid to that list:
<DataGrid VerticalScrollBarVisibility="Auto" ItemsSource="{Binding Employees}"/>
I have the below event triggered when a row is changed in database:
private void OnMyNotificaton(object sender, OracleNotificationEventArgs args)
{
MessageBox.Show("Result set has changed.", "Notification Alert",
MessageBoxButton.OK, MessageBoxImage.Exclamation);
// Append ROWID(s) to the base query to retrieve just modified row(s)
DataRow detailRow = args.Details.Rows[0];
string rowid = detailRow["Rowid"].ToString();
string sqlUpdate = sqlSelect + "where rowid = \'" + rowid + "\'";
// Append on to the sqlUpdate statement if there are additional
// updated rows
for (int i = 1; i < args.Details.Rows.Count; i++)
{
detailRow = args.Details.Rows[i];
rowid = detailRow["Rowid"].ToString();
sqlUpdate = sqlUpdate + " or rowid = \'" + rowid + "\'";
}
// Refresh changed data
using (OracleConnection con2 = new OracleConnection(constr))
{
OracleConnector.List.Clear();
OracleConnector.List = con2.Query<Employees>(sqlUpdate, new DynamicParameters()).AsList();
}
How can I refresh that row in my List and also notify VieModel about the list changes?
As you can see, I am just clearing my list and trying to query again the database with dapper and add the result to the List. The problem is only changed rows are inserted again in the list and also my DataGrid is not updating.
Oracle's example is using an OracleDataAdapter which populates a DataSet and bind it to a DataGrid like so:
// Refresh changed data
OracleConnection con2 = new OracleConnection(constr);
OracleCommand cmd2 = new OracleCommand(sqlUpdate, con2);
con2.Open();
OracleDataAdapter da2 = new OracleDataAdapter(cmd2);
da2.Fill(ds, tablename);
How can I achieve this with Dapper?
EDIT 1
I've read some tips that ObservableCollection is not notifying about changes on items inside and someone suggested to extend it like this:
public class FullyObservableCollection<T> : ObservableCollection<T>, INotifyPropertyChanged
where T : INotifyPropertyChanged
{
/// <summary>
/// Occurs when a property is changed within an item.
/// </summary>
public event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
public FullyObservableCollection() : base()
{ }
public FullyObservableCollection(List<T> list) : base(list)
{
ObserveAll();
}
public FullyObservableCollection(IEnumerable<T> enumerable) : base(enumerable)
{
ObserveAll();
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove ||
e.Action == NotifyCollectionChangedAction.Replace)
{
foreach (T item in e.OldItems)
item.PropertyChanged -= ChildPropertyChanged;
}
if (e.Action == NotifyCollectionChangedAction.Add ||
e.Action == NotifyCollectionChangedAction.Replace)
{
foreach (T item in e.NewItems)
item.PropertyChanged += ChildPropertyChanged;
}
base.OnCollectionChanged(e);
}
protected void OnItemPropertyChanged(ItemPropertyChangedEventArgs e)
{
ItemPropertyChanged?.Invoke(this, e);
}
protected void OnItemPropertyChanged(int index, PropertyChangedEventArgs e)
{
OnItemPropertyChanged(new ItemPropertyChangedEventArgs(index, e));
}
protected override void ClearItems()
{
foreach (T item in Items)
item.PropertyChanged -= ChildPropertyChanged;
base.ClearItems();
}
private void ObserveAll()
{
foreach (T item in Items)
item.PropertyChanged += ChildPropertyChanged;
}
private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
{
T typedSender = (T)sender;
int i = Items.IndexOf(typedSender);
if (i < 0)
throw new ArgumentException("Received property notification from item not in collection");
OnItemPropertyChanged(i, e);
}
}
/// <summary>
/// Provides data for the <see cref="FullyObservableCollection{T}.ItemPropertyChanged"/> event.
/// </summary>
public class ItemPropertyChangedEventArgs : PropertyChangedEventArgs
{
/// <summary>
/// Gets the index in the collection for which the property change has occurred.
/// </summary>
/// <value>
/// Index in parent collection.
/// </value>
public int CollectionIndex { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ItemPropertyChangedEventArgs"/> class.
/// </summary>
/// <param name="index">The index in the collection of changed item.</param>
/// <param name="name">The name of the property that changed.</param>
public ItemPropertyChangedEventArgs(int index, string name) : base(name)
{
CollectionIndex = index;
}
/// <summary>
/// Initializes a new instance of the <see cref="ItemPropertyChangedEventArgs"/> class.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="args">The <see cref="PropertyChangedEventArgs"/> instance containing the event data.</param>
public ItemPropertyChangedEventArgs(int index, PropertyChangedEventArgs args) : this(index, args.PropertyName)
{ }
}
When doing this it seems that the event is triggered when items are added/removed/changed but then again, ViewModel is not aware of this changes and View is not updating.
Here is my model class:
public class Employees : ObservableObject
{
private double _salary;
private int _employeeId;
private string firstName;
private string lastName;
private string email;
private string phoneNumber;
public int EMPLOYEE_ID {
get
{
return _employeeId;
}
set
{
_employeeId = value;
OnPropertyChanged("EMPLOYEE_ID");
}
}
public string FIRST_NAME {
get
{
return firstName;
}
set
{
firstName = value;
OnPropertyChanged("FIRST_NAME");
}
}
public string LAST_NAME
{
get
{
return lastName;
}
set
{
lastName = value;
OnPropertyChanged("LAST_NAME");
}
}
public string EMAIL
{
get
{
return email;
}
set
{
email = value;
OnPropertyChanged("EMAIL");
}
}
public string PHONE_NUMBER
{
get
{
return phoneNumber;
}
set
{
phoneNumber = value;
OnPropertyChanged("PHONE_NUMBER");
}
}
public double SALARY {
get
{
return _salary;
}
set
{
_salary = value;
OnPropertyChanged("SALARY");
}
}
}
and base model class
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (propertyName == null)
{
throw new ArgumentNullException(nameof(propertyName));
}
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The list is populated in OracleConnector.cs
public class OracleConnector
{
public static FullyObservableCollection<Employees> List;
private static string LoadConnectionString(string id = "HR")
{
return ConfigurationManager.ConnectionStrings[id].ConnectionString.ToString();
}
public static FullyObservableCollection<Employees> GetEmployeeRepositorys(string connectionString)
{
using (IDbConnection cnn = new OracleConnection(LoadConnectionString(connectionString)))
{
var dyParam = new OracleDynamicParameters();
try
{
var output = cnn.Query<Employees>(OracleDynamicParameters.sqlSelect, param: dyParam);
List = new FullyObservableCollection<Employees>(output);
}
catch (OracleException ex)
{
MessageBox.Show("Connection to database is not available.\n" + ex.Message, "Database not available", MessageBoxButton.OK, MessageBoxImage.Error);
}
return List;
}
}
}
and changes to dbs are catched in OracleDynamicParameters class
public class OracleDynamicParameters : SqlMapper.IDynamicParameters
{
private readonly DynamicParameters dynamicParameters = new DynamicParameters();
private readonly List<OracleParameter> oracleParameters = new List<OracleParameter>();
public static string tablename = "Employees";
public static string constr = "User Id=hr;Password=hr;Pooling=false;Data Source=ORCL;";
public static string sqlSelect = "select employee_id, first_name, " +
"last_name, salary from employees ";
public static string sql = sqlSelect + "where employee_id < 200";
public static DataSet ds = new DataSet();
public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction, object value = null, int? size = null)
{
OracleParameter oracleParameter;
if (size.HasValue)
{
oracleParameter = new OracleParameter(name, oracleDbType, size.Value, value, direction);
}
else
{
oracleParameter = new OracleParameter(name, oracleDbType, value, direction);
}
oracleParameters.Add(oracleParameter);
}
public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction)
{
var oracleParameter = new OracleParameter(name, oracleDbType, direction);
oracleParameters.Add(oracleParameter);
}
public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{
((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);
if (command is OracleCommand oracleCommand)
{
oracleCommand.AddRowid = true;
OracleDependency dep = new OracleDependency(oracleCommand);
oracleCommand.Notification.IsNotifiedOnce = false;
dep.OnChange += Dep_OnChange;
OracleDataAdapter da = new OracleDataAdapter(oracleCommand)
{
MissingSchemaAction = MissingSchemaAction.AddWithKey
};
oracleCommand.Parameters.AddRange(oracleParameters.ToArray());
}
}
private void Dep_OnChange(object sender, OracleNotificationEventArgs args)
{
MessageBox.Show("Result set has changed.", "Notification Alert", MessageBoxButton.OK, MessageBoxImage.Exclamation);
// Append ROWID(s) to the base query to retrieve just modified row(s)
DataRow detailRow = args.Details.Rows[0];
string rowid = detailRow["Rowid"].ToString();
string sqlUpdate = sqlSelect + "where rowid = \'" + rowid + "\'";
// Append on to the sqlUpdate statement if there are additional
// updated rows
for (int i = 1; i < args.Details.Rows.Count; i++)
{
detailRow = args.Details.Rows[i];
rowid = detailRow["Rowid"].ToString();
sqlUpdate = sqlUpdate + " or rowid = \'" + rowid + "\'";
}
// Refresh changed data
OracleConnection con2 = new OracleConnection(constr);
OracleCommand cmd2 = new OracleCommand(sqlUpdate, con2);
con2.Open();
OracleDataAdapter da2 = new OracleDataAdapter(cmd2);
da2.Fill(ds, tablename);
OracleConnector.List.Clear();
OracleConnector.List = new FullyObservableCollection<Employees>(ds.Tables["Employees"].AsEnumerable().Select(p => new Employees
{
EMPLOYEE_ID = p.Field<int>("EMPLOYEE_ID"),
FIRST_NAME = p.Field<string>("FIRST_NAME"),
LAST_NAME = p.Field<string>("LAST_NAME")
}));
}
}
and finally ShellViewModel
public class ShellViewModel : Conductor<object>
{
private FullyObservableCollection<Employees> _employees;
private Employees _selectedEmployee;
public event NotifyCollectionChangedEventHandler CollectionChanged;
public Employees SelectedEmployee
{
get { return _selectedEmployee; }
set
{
_selectedEmployee = value;
NotifyOfPropertyChange(() => SelectedEmployee);
}
}
public FullyObservableCollection<Employees> Employees
{
get { return _employees; }
set
{
_employees = value;
NotifyOfPropertyChange(() => Employees);
}
}
#region Constructor
public ShellViewModel()
{
_employees = new FullyObservableCollection<Employees>(OracleConnector.GetEmployeeRepositorys("HR"));
}
#endregion
}
What am I doing wrong because I really cannot see where the issue is?
The employee list needs to be an observable list ie that tells a potential listener ie the view model binding, when it has been modified. A plain list does not do that. Change the Employees list to an ObservableCollection<Employee>. In due course you are almost certain to need to implement the IChangeNotification in your Employee class as well when properties are changed.
How should I write this portion of code to serialize all descendant custom types?
The question regards NETJson ( https://github.com/rpgmaker/NetJSON ).
Below the code used to test it.
NETJson Serializer implementation:
class NETJsonFormatter
{
static bool Initialize()
{
NetJSON.NetJSON.IncludeFields = true;
NetJSON.NetJSON.IncludeTypeInformation = true;
return true;
}
static bool Initialized = Initialize();
/// <summary>
/// Serializza un oggetto in un array di byte.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
[System.Diagnostics.DebuggerStepThrough]
static public byte[] Serialize(object obj)
{
return Encoding.UTF8.GetBytes(NetJSON.NetJSON.Serialize(obj));
}
/// <summary>
/// Trasforma un array di byte nell'oggetto originario.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
[System.Diagnostics.DebuggerStepThrough]
static public object Deserialize(byte[] obj)
{
return NetJSON.NetJSON.Deserialize<object>(Encoding.UTF8.GetString(obj));
}
/// <summary>
/// Deserializza un array di byte nel Type desiderato.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
static public T Deserialize<T>(byte[] obj)
{
return NetJSON.NetJSON.Deserialize<T>(Encoding.UTF8.GetString(obj));
}
}
The first object to serialize:
[Serializable]
public class ComplexType
{
public ComplexType()
{
this.Numero = 100;
this.Stringa = "Contenuto";
}
public int Numero { get; set; }
public string Stringa { get; set; }
}
The second object to serialize:
[Serializable]
public class Message_v2 : IMessage
{
public Message_v2()
{
this.Options = new List<string>();
this.Arguments = new Dictionary<string, object>();
}
public bool IsEmpty { get; set; }
public MessageCommand Command { get; set; }
public List<string> Options { get; set; }
/// <summary>
/// Gli Arguments del parser sono sempre KeyValue. Qual'ora mancasse il Value viene inserito null.
/// </summary>
public Dictionary<string, object> Arguments { get; set; }
/*
* Public methods
*/
public void AddOptions(params string[] options)
{
foreach (string option in options)
this.Options.Add(option);
}
public void AddArgument(string key, object value)
{
this.Arguments.Add(key, value);
}
public byte[] ToArray()
{
return NETJsonFormatter.Serialize(this);
}
public string ToXML()
{
throw new NotImplementedException();
}
/// <summary>
/// For debugging purposes.
/// </summary>
/// <returns></returns>
public string ToJSON()
{
return Encoding.UTF8.GetString(NETJsonFormatter.Serialize(this));
}
/*
* Conversions
*/
public static explicit operator Message_v2(byte[] source)
{
try
{
return NETJsonFormatter.Deserialize<Message_v2>(source);
}
catch
{
return null;
}
}
}
And the unit test which fails.
The first test, the one on ComplexObject, passes.
To ensure the data are consistent I use DeepEqual ( https://github.com/jamesfoster/DeepEqual - 'DeepEqual' on NUGet), which provides the method .ShouldDeepEqual used for object comparison.
[TestMethod]
public void CreateAndRetrieveMessage()
{
ComplexType complexArgument = new ComplexType();
byte[] serializedComplexArgument = NETJsonFormatter.Serialize(complexArgument);
ComplexType deserializedComplexArgument = NETJsonFormatter.Deserialize<ComplexType>(serializedComplexArgument);
deserializedComplexArgument.ShouldDeepEqual(complexArgument);
/* ------------------------ */
IMessage message = ProtocolHelper.CreateMessage();
message.Command = MessageCommand.Set;
message.AddOptions("keys");
message.AddArgument("Key1", "Contenuto");
message.AddArgument("Key2", 100);
message.AddArgument("Key3", complexArgument);
// Send over the wire.
byte[] serialized = message.ToArray();
// Get the Message sent.
var deserialized = ProtocolHelper.CreateMessage(serialized);
deserialized.ShouldDeepEqual(message);
}
from the wiki
Dictionary
- Value(Object) supported Dictionary, IList, Primitive Types, and Enums
I think this is your problem, just try to serialize an object dicitionnary, it will fail, but a complexType dictionnary succeed...
ComplexType complexArgument = new ComplexType();
byte[] serializedComplexArgument = NETJsonFormatter.Serialize(complexArgument);
ComplexType deserializedComplexArgument = NETJsonFormatter.Deserialize<ComplexType>(serializedComplexArgument);
deserializedComplexArgument.ShouldDeepEqual(complexArgument);
/* ------------------------ */
var complexTypeDictionnary = new Dictionary<string, ComplexType>();
complexTypeDictionnary.Add("Key3", complexArgument);
byte[] serializedDic2 = NETJsonFormatter.Serialize(complexTypeDictionnary);
var deserializeDictionnary2 = NETJsonFormatter.Deserialize<Dictionary<string, ComplexType>>(serializedDic2);
deserializeDictionnary2.ShouldDeepEqual(complexTypeDictionnary); // works
/* ------------------------ */
var objectDictionnary = new Dictionary<string, object>();
objectDictionnary.Add("Key1", "Contenuto");
objectDictionnary.Add("Key2", 100);
objectDictionnary.Add("Key3", complexArgument);
byte[] serializedDic = NETJsonFormatter.Serialize(objectDictionnary);
var deserializeDictionnary = NETJsonFormatter.Deserialize<Dictionary<string, object>>(serializedDic);
deserializeDictionnary.ShouldDeepEqual(objectDictionnary); // doesn't work
Edit
I used to play with NewtonSoft for Json serialization, maybe your searching for performance improvement or other but it works well with it ;)
/* ------------------------ */
var objectDictionnary = new Dictionary<string, object>();
objectDictionnary.Add("Key1", "Contenuto");
objectDictionnary.Add("Key2", 100);
objectDictionnary.Add("Key3", complexArgument);
byte[] serializedDicNewton = Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject( objectDictionnary));
var deserializeDictionnaryNewton = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(Encoding.UTF8.GetString(serializedDicNewton));
deserializeDictionnaryNewton.ShouldDeepEqual(objectDictionnary); // works too
NETJson doesn't properly serialize a Dictionary cause Object it's the base Type but it can serialize a lot of primitives (included byte array) so I found a solution (well, a workaround for now cause my eyes bleed looking at code - but it works great).
Class Helper for NETJson
class NETJsonFormatter
{
public NETJsonFormatter() { }
static bool Initialize()
{
NetJSON.NetJSON.IncludeFields = true;
NetJSON.NetJSON.IncludeTypeInformation = true;
return true;
}
static bool Initialized = Initialize();
/// <summary>
/// Serializza un oggetto in un array di byte.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
//[System.Diagnostics.DebuggerStepThrough]
static public byte[] SerializeWithType(object obj)
{
return Encoding.UTF8.GetBytes(NetJSON.NetJSON.Serialize(obj.GetType(), obj));
}
/// <summary>
/// Trasforma un array di byte nell'oggetto originario.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
//[System.Diagnostics.DebuggerStepThrough]
static public object DeserializeWithType(Type type, byte[] obj)
{
return NetJSON.NetJSON.Deserialize(type, Encoding.UTF8.GetString(obj));
}
static public byte[] Serialize<T>(T obj)
{
return Encoding.UTF8.GetBytes(NetJSON.NetJSON.Serialize(obj));
}
/// <summary>
/// Deserializza un array di byte nel Type desiderato.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
static public T Deserialize<T>(byte[] obj)
{
return NetJSON.NetJSON.Deserialize<T>(Encoding.UTF8.GetString(obj));
}
}
Second object to serialize (which didn't succeed on already unit test)
[Serializable]
public class Message_v2 //: IMessage
{
public Message_v2()
{
this.Options = new List<string>();
this.Arguments = new Dictionary<string, object>();
this.ValuesForNETJson = new Dictionary<string, byte[]>();
this.TypesForNETJson = new Dictionary<string, Type>();
}
public bool IsEmpty { get; set; }
public MessageCommand Command { get; set; }
public List<string> Options { get; set; }
/// <summary>
/// Gli Arguments del parser sono sempre KeyValue. Qual'ora mancasse il Value viene inserito null.
/// </summary>
public Dictionary<string, object> Arguments { get; set; }
/// <summary>
/// Serializzo gli Arguments in byte array.
/// string - Key di Arguments[n]
/// byte[] - contenuto serializzato di Arguments[n]
/// </summary>
public IDictionary<string, byte[]> ValuesForNETJson { get; set; }
public IDictionary<string, Type> TypesForNETJson { get; set; }
/*
* Public methods
*/
public void AddOptions(params string[] options)
{
foreach (string option in options)
this.Options.Add(option);
}
public void AddArgument(string key, object value)
{
this.Arguments.Add(key, value);
this.ValuesForNETJson.Add(key, NETJsonFormatter.SerializeWithType(value));
this.TypesForNETJson.Add(key, value.GetType());
}
public byte[] ToArray()
{
//this.Arguments.ToDictionary(x => x.Value == null);
return NETJsonFormatter.Serialize(this);
}
/*
* Conversions
*/
public static explicit operator Message_v2(byte[] source)
{
try
{
Message_v2 message = NETJsonFormatter.Deserialize<Message_v2>(source);
int count = message.ValuesForNETJson.Count;
for (int i = 0; i < count; i++)
{
string key = message.Arguments.ElementAt(i).Key;
Type type = message.TypesForNETJson.ElementAt(i).Value;
byte[] value = message.ValuesForNETJson[key];
message.Arguments[key] = NETJsonFormatter.DeserializeWithType(type, value);
}
return message;
}
catch (Exception ex)
{
return null;
}
}
}
For sure my solution will be reworked cause it's not optimal, far from that.
Adding two Dictionary to the class make it grow and be larger than same class serialized using ProtoBuf, and make it slower.
I’m experiencing a problem with the longlistselector witch is a bit strange... I’m reading a list asynchronously on a multi Pivot page, and if I don’t change the pivot until the resulto f the list Reading, it will create the contacts list successfuly ( in a longlistselector on the pivot item number 3 ) and when I go to that pivot the contacts list is displayed withou any problems, but when I open the Page and change the pivot before the longlistselector is created when the asychronous function returns the results and fills the longlistselector on pivot no.3 the contacts wont get updated ( when I go to pivot 3 no contacts are shown)...
I’ll post my code so you can have a clearer picture of the situation and maybe figure out what is happening.
The code is based in the PhoneToolkit LongListSelector example (buddies list)
public partial class Feeds : PhoneApplicationPage
{
bool isLoading = false;
bool loadingFilmates = true;
public Feeds()
{
InitializeComponent();
// ...
Loaded += FeedsPage_Loaded;
SystemTray.ProgressIndicator = new ProgressIndicator();
DataContext = this;
getFilmatesList();
longlistFilmates.SelectionChanged += FilmateSelectionChanged;
// ...
}
private async void getFilmatesList()
{
Feed userFilmates = await Feed.GetFilmates(App.Current.AppUser, App.Current.WSConfig, 0, "", 10000); // read feeds from webservice
Filmates = AlphaKeyGroup<StayObjectFilmates>.CreateGroups(AllFilmates.GetCurrent(userFilmates), CultureInfo.CurrentUICulture, (p) => { return p.Name; }, true);
//longlistFilmates.Visibility = System.Windows.Visibility.Collapsed;
longlistFilmates.Visibility = System.Windows.Visibility.Visible;
longlistFilmates.UseLayoutRounding = true;
pivotFeed.Visibility = System.Windows.Visibility.Collapsed;
pivotFeed.Visibility = System.Windows.Visibility.Visible;
}
}
Notice that I’ve even tried changing the Visibility property when it loads to force a re-render on the screen and it didn’t work.
This is the StayObjectFilmates class:
public class StayObjectFilmates
{
public string Img { get; private set; }
public string Name { get; private set; }
public string UserId { get; private set; }
public string Id { get; set; }
public User user { get; set; }
public StayObjectFilmates()
{
//Img = "";
//Name = "";
//Id = "";
}
public StayObjectFilmates(string p_img, string p_name, string p_Id)
{
Img = p_img;
Name = p_name;
UserId = p_Id;
}
public StayObjectFilmates(User p_user)
{
user = p_user;
}
public static string GetNameKey(StayObjectFilmates filmate)
{
char key = char.ToLower(filmate.Name[0]);
if (key < 'a' || key > 'z')
{
key = '#';
}
return key.ToString();
}
public static int CompareByName(object obj1, object obj2)
{
StayObjectFilmates p1 = (StayObjectFilmates)obj1;
StayObjectFilmates p2 = (StayObjectFilmates)obj2;
int result = p1.Name.CompareTo(p2.Name);
if (result == 0)
{
result = p1.Img.CompareTo(p2.Img);
}
return result;
}
}
This is the AllFilmates class:
public class AllFilmates : IEnumerable<StayObjectFilmates>
{
private static Dictionary<int, StayObjectFilmates> _filmateLookup;
private static AllFilmates _instance;
private Feed filmates;
// public List<StayObjectFilmates> Filmates { get; private set; }
public static AllFilmates GetCurrent(Feed p_filmates)
{
if (_instance == null)
{
_instance = new AllFilmates();
}
_instance.filmates = p_filmates;
return _instance;
}
public static AllFilmates Current
{
get
{
return _instance ?? (_instance = new AllFilmates());
}
}
public StayObjectFilmates this[int index]
{
get
{
StayObjectFilmates filmate;
_filmateLookup.TryGetValue(index, out filmate);
return filmate;
}
}
#region IEnumerable<StayObjectFilmates> Members
public IEnumerator<StayObjectFilmates> GetEnumerator()
{
EnsureData();
return _filmateLookup.Values.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
EnsureData();
return _filmateLookup.Values.GetEnumerator();
}
#endregion
private void EnsureData()
{
if (_filmateLookup == null)
{
_filmateLookup = new Dictionary<int, StayObjectFilmates>();
if (filmates != null)
{
int i = 0;
foreach (var item in filmates.itemsList)
{
User friend = item as User;
string userphoto = (friend.photo == null) ? "Images/avatar.jpg" : friend.photo;
StayObjectFilmates f = new StayObjectFilmates(userphoto, friend.fullName, i.ToString());
_filmateLookup[i] = f;
i++;
}
}
}
}
}
And this is the AlphaKeyGroup.cs file :
public class AlphaKeyGroup<T> : List<T>
{
private const string GlobeGroupKey = "\uD83C\uDF10";
/// <summary>
/// The delegate that is used to get the key information.
/// </summary>
/// <param name="item">An object of type T</param>
/// <returns>The key value to use for this object</returns>
public delegate string GetKeyDelegate(T item);
/// <summary>
/// The Key of this group.
/// </summary>
public string Key { get; private set; }
/// <summary>
/// Public constructor.
/// </summary>
/// <param name="key">The key for this group.</param>
public AlphaKeyGroup(string key)
{
Key = key;
}
public AlphaKeyGroup(IGrouping<string, T> grouping)
{
Key = grouping.Key;
this.AddRange(grouping);
}
/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="slg">The </param>
/// <returns>Theitems source for a LongListSelector</returns>
private static List<AlphaKeyGroup<T>> CreateGroups(SortedLocaleGrouping slg)
{
List<AlphaKeyGroup<T>> list = new List<AlphaKeyGroup<T>>();
foreach (string key in slg.GroupDisplayNames)
{
if (key == "...")
{
list.Add(new AlphaKeyGroup<T>(GlobeGroupKey));
}
else
{
list.Add(new AlphaKeyGroup<T>(key));
}
}
return list;
}
/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="items">The items to place in the groups.</param>
/// <param name="ci">The CultureInfo to group and sort by.</param>
/// <param name="getKey">A delegate to get the key from an item.</param>
/// <param name="sort">Will sort the data if true.</param>
/// <returns>An items source for a LongListSelector</returns>
public static List<AlphaKeyGroup<T>> CreateGroups(IEnumerable<T> items, CultureInfo ci, GetKeyDelegate getKey, bool sort)
{
SortedLocaleGrouping slg = new SortedLocaleGrouping(ci);
List<AlphaKeyGroup<T>> list = CreateGroups(slg);
foreach (T item in items)
{
int index = 0;
if (slg.SupportsPhonetics)
{
//check if your database has yomi string for item
//if it does not, then do you want to generate Yomi or ask the user for this item.
//index = slg.GetGroupIndex(getKey(Yomiof(item)));
}
else
{
index = slg.GetGroupIndex(getKey(item));
}
if (index >= 0 && index < list.Count)
{
list[index].Add(item);
}
}
if (sort)
{
foreach (AlphaKeyGroup<T> group in list)
{
group.Sort((c0, c1) => { return ci.CompareInfo.Compare(getKey(c0), getKey(c1)); });
}
}
return list;
}
}
The FilmatesInGroup.cs and FilmatesByName.cs is the same as PeopleInGroup.cs and PeopleByFirstName.cs in the PhoneToolKit example with the names adapted.
The longlistFilmates LongListSelector Object is inserted directly inside the PivotItem no.3 ( no Grid and no ScrollView )
Thanks in advance for any help!
I have own collection DataContracts:
[DataContract]
public class DzieckoAndOpiekun
{
public DzieckoAndOpiekun() { }
public DzieckoAndOpiekun(string dzImie, string dzNazwisko, string opImie, string opNazwisko)
{
this.DzieckoImie = dzImie;
this.DzieckoImie = dzNazwisko;
this.OpiekunImie = opImie;
this.OpiekunNazwisko = opNazwisko;
}
/// <summary>
/// Field for Dziecko's Surname
/// </summary>
private string dzieckoNazwisko;
/// <summary>
/// Field for Dziecko's Name
/// </summary>
private string dzieckoImie;
/// <summary>
/// Field for Opiekun's Surname
/// </summary>
private string opiekunNazwisko;
/// <summary>
/// Field for Opiekun's Name
/// </summary>
private string opiekunImie;
/// <summary>
/// Gets or sets the dziecko nazwisko.
/// </summary>
/// <value>The dziecko nazwisko.</value>
[DataMember]
public virtual string DzieckoNazwisko
{
get { return this.dzieckoNazwisko; }
set { this.dzieckoNazwisko = value; }
}
/// <summary>
/// Gets or sets the dziecko imie.
/// </summary>
/// <value>The dziecko imie.</value>
[DataMember]
public virtual string DzieckoImie
{
get { return this.dzieckoImie; }
set { this.dzieckoImie = value; }
}
/// <summary>
/// Gets or sets the opiekun nazwisko.
/// </summary>
/// <value>The opiekun nazwisko.</value>
[DataMember]
public virtual string OpiekunNazwisko
{
get { return this.opiekunNazwisko; }
set { this.opiekunNazwisko = value; }
}
/// <summary>
/// Gets or sets the opiekun imie.
/// </summary>
/// <value>The opiekun imie.</value>
[DataMember]
public virtual string OpiekunImie
{
get { return this.opiekunImie; }
set { this.opiekunImie = value; }
}
}
[DataContract]
public class DzieckoAndOpiekunCollection : IEnumerable<DzieckoAndOpiekun>, IList<DzieckoAndOpiekun>
{
[DataMember]
List<DzieckoAndOpiekun> collection = new List<DzieckoAndOpiekun>();
[DataMember]
public List<DzieckoAndOpiekun> Collection
{
get { return collection; }
set { collection = value; }
}
public IEnumerator<DzieckoAndOpiekun> GetEnumerator()
{
return collection.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new System.NotImplementedException();
}
public int IndexOf(DzieckoAndOpiekun item)
{
return collection.IndexOf(item);
}
public void Insert(int index, DzieckoAndOpiekun item)
{
collection.Insert(index, item);
}
public void RemoveAt(int index)
{
collection.RemoveAt(index);
}
public DzieckoAndOpiekun this[int index]
{
get
{
return collection[index];
}
set
{
collection[index] = value;
}
}
public void Add(DzieckoAndOpiekun item)
{
this.collection.Add(item);
}
public void AddRange(IList<DzieckoAndOpiekun> collection)
{
this.collection.AddRange(collection);
}
public void Clear()
{
collection.Clear();
}
public bool Contains(DzieckoAndOpiekun item)
{
return collection.Contains(item);
}
public void CopyTo(DzieckoAndOpiekun[] array, int arrayIndex)
{
this.collection.CopyTo(array, arrayIndex);
}
public int Count
{
get { return this.collection.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(DzieckoAndOpiekun item)
{
return this.collection.Remove(item);
}
}
And use it in my ServiceContract implementation:
public DzieckoAndOpiekunCollection GetChildAndOpiekunByFirstnameLastname(string firstname, string lastname)
{
DataTransfer.ChargeInSchoolEntities db = new DataTransfer.ChargeInSchoolEntities();
DzieckoAndOpiekunCollection result = new DzieckoAndOpiekunCollection();
if (firstname == null && lastname != null)
{
IQueryable<DzieckoAndOpiekun> resultV = from p in db.Dziecko
where lastname == p.Nazwisko
select new DzieckoAndOpiekun(
p.Imie,
p.Nazwisko,
p.Opiekun.Imie,
p.Opiekun.Nazwisko);
List<DzieckoAndOpiekun> l = resultV.ToList();
result.AddRange(l);
}
if (firstname != null && lastname != null)
{
IQueryable<DzieckoAndOpiekun> resultV = from p in db.Dziecko
where firstname == p.Imie && lastname == p.Nazwisko
select new DzieckoAndOpiekun(
p.Imie,
p.Nazwisko,
p.Opiekun.Imie,
p.Opiekun.Nazwisko);
List<DzieckoAndOpiekun> l = resultV.ToList();
result.AddRange(l);
}
if (firstname != null && lastname == null)
{
IQueryable<DzieckoAndOpiekun> resultV = from p in db.Dziecko
where firstname == p.Imie
select new DzieckoAndOpiekun(
p.Imie,
p.Nazwisko,
p.Opiekun.Imie,
p.Opiekun.Nazwisko);
List<DzieckoAndOpiekun> l = resultV.ToList();
result.AddRange(l);
}
return result;
}
Do I something wrong in creating own data contract and sending it ? I ask cause I have errors when consuming method :/ :
An exception occurred during the operation, making the result invalid.
Check InnerException for exception
details.
at
System.ComponentModel.AsyncCompletedEventArgs.RaiseExceptionIfNecessary()
at
SecretaryAppNav.ClientService.GetChildAndOpiekunByFirstnameLastnameCompletedEventArgs.get_Result() at
SecretaryAppNav.Views.FindChild.Client_GetChildAndOpiekunByFirstnameLastnameCompleted(Object
sender,
GetChildAndOpiekunByFirstnameLastnameCompletedEventArgs
e)
at SecretaryAppNav.ClientService.Service1Client.OnGetChildAndOpiekunByFirstnameLastnameCompleted(Object
state)
I can't guarantee that this would work, but you are sending the collection twice:
[DataContract]
public class DzieckoAndOpiekunCollection :
IEnumerable<DzieckoAndOpiekun>, IList<DzieckoAndOpiekun>
{
[DataMember]
List<DzieckoAndOpiekun> collection = new List<DzieckoAndOpiekun>();
[DataMember]
public List<DzieckoAndOpiekun> Collection
{
get { return collection; }
set { collection = value; }
}
...
You should probably only place the DataMember attribute on the field or the property, but not both, since they are exposing the same value.
I solve the problem with CollectionDataContract attribute ;)
...apart from the obvious looping through the list and a dirty great case statement!
I've turned over a few Linq queries in my head but nothing seems to get anywhere close.
Here's the an example DTO if it helps:
class ClientCompany
{
public string Title { get; private set; }
public string Forenames { get; private set; }
public string Surname { get; private set; }
public string EmailAddress { get; private set; }
public string TelephoneNumber { get; private set; }
public string AlternativeTelephoneNumber { get; private set; }
public string Address1 { get; private set; }
public string Address2 { get; private set; }
public string TownOrDistrict { get; private set; }
public string CountyOrState { get; private set; }
public string PostCode { get; private set; }
}
We have no control over the fact that we're getting the data in as KV pairs, I'm afraid.
and whilst there is an effective mapping of each KV pair to each property and I do know the keys in advance they're not named the same as the DTO.
Here is an elegant, extensible, maintainable and blazingly fast solution for loading DTOs from Dictionaries.
Create a console app and add these two files. The rest is self documenting.
The salient points:
simple, brief and maintainable mapping classes.
efficient object rehydration using dynamic methods.
NOTE: If you copied the previous DynamicProperties.cs, you will want to get this one. I added a flag to allow generation of private setters that was not in the previous version.
Cheers.
program.cs
using System.Collections.Generic;
using System.Diagnostics;
using Salient.Reflection;
namespace KVDTO
{
/// <summary>
/// This is our DTO
/// </summary>
public class ClientCompany
{
public string Address1 { get; private set; }
public string Address2 { get; private set; }
public string AlternativeTelephoneNumber { get; private set; }
public string CountyOrState { get; private set; }
public string EmailAddress { get; private set; }
public string Forenames { get; private set; }
public string PostCode { get; private set; }
public string Surname { get; private set; }
public string TelephoneNumber { get; private set; }
public string Title { get; private set; }
public string TownOrDistrict { get; private set; }
}
/// <summary>
/// This is our DTO Map
/// </summary>
public sealed class ClientCompanyMapping : KeyValueDtoMap<ClientCompany>
{
static ClientCompanyMapping()
{
AddMapping("Title", "Greeting");
AddMapping("Forenames", "First");
AddMapping("Surname", "Last");
AddMapping("EmailAddress", "eMail");
AddMapping("TelephoneNumber", "Phone");
AddMapping("AlternativeTelephoneNumber", "Phone2");
AddMapping("Address1", "Address1");
AddMapping("Address2", "Address2");
AddMapping("TownOrDistrict", "City");
AddMapping("CountyOrState", "State");
AddMapping("PostCode", "Zip");
}
}
internal class Program
{
private const string Address1 = "1243 Easy Street";
private const string CountyOrState = "Az";
private const string EmailAddress = "nunya#bidnis.com";
private const string Forenames = "Sky";
private const string PostCode = "85282";
private const string Surname = "Sanders";
private const string TelephoneNumber = "800-555-1212";
private const string Title = "Mr.";
private const string TownOrDistrict = "Tempe";
private static void Main(string[] args)
{
// this represents our input data, some discrepancies
// introduced to demonstrate functionality of the map
// the keys differ from the dto property names
// there are missing properties
// there are unrecognized properties
var input = new Dictionary<string, string>
{
{"Greeting", Title},
{"First", Forenames},
{"Last", Surname},
{"eMail", EmailAddress},
{"Phone", TelephoneNumber},
// missing from this input {"Phone2", ""},
{"Address1", Address1},
// missing from this input {"Address2", ""},
{"City", TownOrDistrict},
{"State", CountyOrState},
{"Zip", PostCode},
{"SomeOtherFieldWeDontCareAbout", "qwerty"}
};
// rehydration is simple and FAST
// instantiate a map. You could store instances in a dictionary
// but it is not really necessary for performance as all of the
// work is done in the static constructors, so no matter how many
// times you 'new' a map, it is only ever built once.
var map = new ClientCompanyMapping();
// do the work.
ClientCompany myDto = map.Load(input);
// test
Debug.Assert(myDto.Address1 == Address1, "Address1");
Debug.Assert(myDto.Address2 == null, "Address2");
Debug.Assert(myDto.AlternativeTelephoneNumber == null, "AlternativeTelephoneNumber");
Debug.Assert(myDto.CountyOrState == CountyOrState, "CountyOrState");
Debug.Assert(myDto.EmailAddress == EmailAddress, "EmailAddress");
Debug.Assert(myDto.Forenames == Forenames, "Forenames");
Debug.Assert(myDto.PostCode == PostCode, "PostCode");
Debug.Assert(myDto.Surname == Surname, "Surname");
Debug.Assert(myDto.TelephoneNumber == TelephoneNumber, "TelephoneNumber");
Debug.Assert(myDto.Title == Title, "Title");
Debug.Assert(myDto.TownOrDistrict == TownOrDistrict, "TownOrDistrict");
}
}
/// <summary>
/// Base mapper class.
/// </summary>
/// <typeparam name="T"></typeparam>
public class KeyValueDtoMap<T> where T : class, new()
{
private static readonly List<DynamicProperties.Property> Props;
private static readonly Dictionary<string, string> KvMap;
static KeyValueDtoMap()
{
// this property collection is built only once
Props = new List<DynamicProperties.Property>(DynamicProperties.CreatePropertyMethods(typeof(T)));
KvMap=new Dictionary<string, string>();
}
/// <summary>
/// Adds a mapping between a DTO property and a KeyValue pair
/// </summary>
/// <param name="dtoPropertyName">The name of the DTO property</param>
/// <param name="inputKey">The expected input key</param>
protected static void AddMapping(string dtoPropertyName,string inputKey)
{
KvMap.Add(dtoPropertyName,inputKey);
}
/// <summary>
/// Creates and loads a DTO from a Dictionary
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public T Load(Dictionary<string, string> input)
{
var result = new T();
Props.ForEach(p =>
{
string inputKey = KvMap[p.Info.Name];
if (input.ContainsKey(inputKey))
{
p.Setter.Invoke(result, input[inputKey]);
}
});
return result;
}
}
}
DynamicProperties.cs
/*!
* Project: Salient.Reflection
* File : DynamicProperties.cs
* http://spikes.codeplex.com
*
* Copyright 2010, Sky Sanders
* Dual licensed under the MIT or GPL Version 2 licenses.
* See LICENSE.TXT
* Date: Sat Mar 28 2010
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace Salient.Reflection
{
/// <summary>
/// Gets IL setters and getters for a property.
/// </summary>
public static class DynamicProperties
{
#region Delegates
public delegate object GenericGetter(object target);
public delegate void GenericSetter(object target, object value);
#endregion
public static IList<Property> CreatePropertyMethods(Type T)
{
var returnValue = new List<Property>();
foreach (PropertyInfo prop in T.GetProperties())
{
returnValue.Add(new Property(prop));
}
return returnValue;
}
public static IList<Property> CreatePropertyMethods<T>()
{
var returnValue = new List<Property>();
foreach (PropertyInfo prop in typeof (T).GetProperties())
{
returnValue.Add(new Property(prop));
}
return returnValue;
}
/// <summary>
/// Creates a dynamic setter for the property
/// </summary>
/// <param name="propertyInfo"></param>
/// <returns></returns>
/// <source>
/// http://jachman.wordpress.com/2006/08/22/2000-faster-using-dynamic-method-calls/
/// </source>
public static GenericSetter CreateSetMethod(PropertyInfo propertyInfo)
{
/*
* If there's no setter return null
*/
MethodInfo setMethod = propertyInfo.GetSetMethod(true);
if (setMethod == null)
return null;
/*
* Create the dynamic method
*/
var arguments = new Type[2];
arguments[0] = arguments[1] = typeof (object);
var setter = new DynamicMethod(
String.Concat("_Set", propertyInfo.Name, "_"),
typeof (void), arguments, propertyInfo.DeclaringType);
ILGenerator generator = setter.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
generator.Emit(OpCodes.Ldarg_1);
if (propertyInfo.PropertyType.IsClass)
generator.Emit(OpCodes.Castclass, propertyInfo.PropertyType);
else
generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
generator.EmitCall(OpCodes.Callvirt, setMethod, null);
generator.Emit(OpCodes.Ret);
/*
* Create the delegate and return it
*/
return (GenericSetter) setter.CreateDelegate(typeof (GenericSetter));
}
/// <summary>
/// Creates a dynamic getter for the property
/// </summary>
/// <param name="propertyInfo"></param>
/// <returns></returns>
/// <source>
/// http://jachman.wordpress.com/2006/08/22/2000-faster-using-dynamic-method-calls/
/// </source>
public static GenericGetter CreateGetMethod(PropertyInfo propertyInfo)
{
/*
* If there's no getter return null
*/
MethodInfo getMethod = propertyInfo.GetGetMethod(true);
if (getMethod == null)
return null;
/*
* Create the dynamic method
*/
var arguments = new Type[1];
arguments[0] = typeof (object);
var getter = new DynamicMethod(
String.Concat("_Get", propertyInfo.Name, "_"),
typeof (object), arguments, propertyInfo.DeclaringType);
ILGenerator generator = getter.GetILGenerator();
generator.DeclareLocal(typeof (object));
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
generator.EmitCall(OpCodes.Callvirt, getMethod, null);
if (!propertyInfo.PropertyType.IsClass)
generator.Emit(OpCodes.Box, propertyInfo.PropertyType);
generator.Emit(OpCodes.Ret);
/*
* Create the delegate and return it
*/
return (GenericGetter) getter.CreateDelegate(typeof (GenericGetter));
}
#region Nested type: Property
public class Property
{
public GenericGetter Getter;
public PropertyInfo Info;
public GenericSetter Setter;
public Property(PropertyInfo info)
{
Info = info;
Setter = CreateSetMethod(info);
Getter = CreateGetMethod(info);
}
}
#endregion
}
}
If you can get the data to look like ['Title':'Mr', 'Forenames':'John', 'Surname':'Doe',...], then you should be able to JSON deserialize the kvp into your source object.
In such a case, I would use reflection to map the Key-Value pairs to the object properties. For an example, check the accepted answer on this SO question
Alternatively if you don't want to go with reflection you could use this (this wont' work blazingly fast):
var ccd = new List<KeyValuePair<string, string>>();
ccd.Add(new KeyValuePair<string, string>("Title", ""));
ccd.Add(new KeyValuePair<string, string>("Forenames", ""));
ccd.Add(new KeyValuePair<string, string>("Surname", ""));
ccd.Add(new KeyValuePair<string, string>("EmailAddress", ""));
ccd.Add(new KeyValuePair<string, string>("TelephoneNumber", ""));
ccd.Add(new KeyValuePair<string, string>("AlternativeTelephoneNumber", ""));
ccd.Add(new KeyValuePair<string, string>("Address1", ""));
ccd.Add(new KeyValuePair<string, string>("Address2", ""));
ccd.Add(new KeyValuePair<string, string>("TownOrDistrict", ""));
ccd.Add(new KeyValuePair<string, string>("CountyOrState", ""));
ccd.Add(new KeyValuePair<string, string>("PostCode", ""));
var data = new List<List<KeyValuePair<string, string>>> { ccd, ccd, ccd };
var companies = from d in data
select new ClientCompany {
Title = d.FirstOrDefault(k => k.Key == "Title").Value,
Forenames = d.FirstOrDefault(k => k.Key == "Forenames").Value,
Surname = d.FirstOrDefault(k => k.Key == "Surname").Value,
EmailAddress = d.FirstOrDefault(k => k.Key == "EmailAddress").Value,
TelephoneNumber = d.FirstOrDefault(k => k.Key == "TelephoneNumber").Value,
AlternativeTelephoneNumber = d.FirstOrDefault(k => k.Key == "AlternativeTelephoneNumber").Value,
Address1 = d.FirstOrDefault(k => k.Key == "Address1").Value,
Address2 = d.FirstOrDefault(k => k.Key == "Address2").Value,
TownOrDistrict = d.FirstOrDefault(k => k.Key == "TownOrDistrict").Value,
CountyOrState = d.FirstOrDefault(k => k.Key == "CountyOrState").Value,
PostCode = d.FirstOrDefault(k => k.Key == "PostCode").Value,
};