SQL Datareader is not holding the values - c#

I have been working with a project for last 4 months. We are using a custom framework for the development. The problem I am talking about, was working for all other classes. But for the first time I am facing this weird incident. Now Straight to break point.
My framework code is like
public static List<ViewNotSetBillableCoursesEntity> GetAllNotSetBillableCources()
{
try
{
List<ViewNotSetBillableCoursesEntity> entities = new List<ViewNotSetBillableCoursesEntity>();
string command = SELECT;
SqlConnection sqlConnection = MSSqlConnectionHandler.GetConnection();
SqlDataReader dataReader = QueryHandler.ExecuteSelect(command, sqlConnection);
entities = Maps(dataReader);
dataReader.Close();
return entities;
}
catch (Exception exception)
{
throw exception;
}
}
In the above method the dataReader is sent to Maps method.
The Maps method is ......
private static List<ViewNotSetBillableCoursesEntity> Maps(SqlDataReader theReader)
{
SQLNullHandler nullHandler = new SQLNullHandler(theReader);
// the incident is happening here, the SQLNullHandler is given below.
List<ViewNotSetBillableCoursesEntity> entities = null;
while (theReader.Read())
{
if (entities == null)
{
entities = new List<ViewNotSetBillableCoursesEntity>();
}
ViewNotSetBillableCoursesEntity entity = Mapper(nullHandler);
entities.Add(entity);
}
return entities;
}
The SQLNullHandler is given below:
puplic Class SQLNullHandler
{
private IDataReader _reader;
public SQLNullHandler(IDataReader reader)
{
_reader = reader;
}
#region Get Null value
public static object GetNullValue(int Value)
{
if(Value==0)
{
return null;
}
else
{
return Value;
}
}
public static object GetNullValue(double Value)
{
if (Value == 0)
{
return null;
}
else
{
return Value;
}
}
public static object GetNullValue(decimal Value)
{
if (Value == 0)
{
return null;
}
else
{
return Value;
}
}
public static object GetNullValue(DateTime Value)
{
if(DateTime.MinValue==Value)
{
return null;
}
else
{
return Value;
}
}
public static object GetNullValue(string Value)
{
if(Value.Length<=0)
{
return null;
}
else
{
return Value;
}
}
#endregion
public IDataReader Reader
{
get{return _reader;}
}
public bool IsNull(int index)
{
return _reader.IsDBNull(index);
}
public int GetInt32(int i)
{
return _reader.IsDBNull(i)? 0 : _reader.GetInt32(i);
}
public byte GetByte(int i)
{
return _reader.IsDBNull(i)? (byte)0 : _reader.GetByte(i);
}
//and so on for all possible type for this app
}
The Funny thing is for all these classes these methods and lines of code work very fine, but in this scenario after the line SQLNullHandler nullHandler = new SQLNullHandler(theReader); the datareder becomes empty.
My questions are
Why is this Happening and next,
what can be done to solve this problem?

Related

invoke method with list of parameter c#

I have a class:
namespace TestLib1
public class TestLib1
{
public IEnumerable<TestedParameter> Measure1(IEnumerable<TestedParameter> _inParameters)
{ // this is just to do something
foreach (TestedParameter item in _inParameters)
{
item.Param_Description = "TestParam";
}
return _inParameters;
}
}
that contains class from
public class Acceptance_Criteria
{
private double _lowest;
private double _highest;
public bool Compare(double _verifiedValue)
{
if ((_verifiedValue >= _lowest) && (_verifiedValue <= _highest))
{
return true;
}
else
{
return false;
}
}
public double Acceptance_Lowest { get =>_lowest;set =>_lowest = value; }
public double Acceptance_Highest {get=>_highest;set =>_highest = value; }
}
public class TestedParameter : Acceptance_Criteria
{
private string _param_Description;
private double _value;
private string _units;
private bool _value_matches_acceptance_critera;
private bool _value_checked_against_criteria;
private string pass_fail_reason;
public string Param_Description { get => _param_Description; set => _param_Description = value; }
public double Value { get => _value; set => _value = value; }
public string Units { get => _units; set => _units = value; }
public bool Value_matches_acceptance_critera { get => _value_matches_acceptance_critera; set => _value_matches_acceptance_critera = value; }
public bool Value_checked_against_criteria { get => _value_checked_against_criteria; set => _value_checked_against_criteria = value; }
public string Pass_fail_reason { get => pass_fail_reason; set => pass_fail_reason = value; }
}
and I placed TestLib in DLL and call it from other file :
Assembly SampleAssembly = Assembly.LoadFrom("TestLib1.dll");
Type[] types2 = SampleAssembly.GetTypes();//e
Type SearchedType = null;
foreach (Type type in types2)
{
if (type.FullName.Contains("TestLib1.TestLib1")) { SearchedType = SampleAssembly.GetType(type.FullName); }
}
if (SearchedType != null)
{
MethodInfo Method = SearchedType.GetMethod("Measure1");
if (Method != null)
{
object result = null;
ParameterInfo[] parameters = Method.GetParameters();
object classInstance = Activator.CreateInstance(SearchedType, null);
if (parameters.Length == 0)
{
//This works fine
result = Method.Invoke(classInstance, null);
}
else
{
List<TestedParameter> parametersIn = new List<TestedParameter>();
TestedParameter param1 = new TestedParameter();
param1.Param_Description = "testparam1"; // GET NAME FROM XML
parametersIn.Add(param1);
IEnumerable<TestedParameter> enumarableList = parametersIn.AsEnumerable();
object[] parametersArrayx = new object[] { enumarableList };
object resx = Method.Invoke(classInstance, parametersArrayx.ToArray());
List<TestedParameter> res = (List<TestedParameter>)resx;
}
}
}
When I execute Method.Invoke
I get thie exception:
System.ArgumentException:
'Object of type 'System.Collections.Generic.List`1[WindowsFormsApp1.TestedParameter]'
cannot be converted to type 'System.Collections.Generic.List`1[WindowsFormsApp1.TestedParameter]'.'
I also tried to push parametersArrayx without ToArray() but it also didnt work
Can anyone look a the code a let me know what do I do wrong ?
I want to execute code from DLL lib with list of parameters pushed (later I want to import those parameters from XML file.)
Thank You :)
SearchedType.GetMethod("Measure1");
convert to
SearchedType.GetMethod("Measure1", new Type[] { typeof(List<Filter>) });

populate IDataReader without reflection using any (specified) object Collection

I struggling to get something done when it suppose to be very easy to implement
say I have a car object
public class tblCarObj
{
public String Model;
public Int32 Year;
}
within these lines there is an implementation of reflection that is based on the fact that the object members are unknown (is this the only reason?)
which is nice for a generic functionality, but I am sacrificing functionality for performance if needed. and I wonder why both Links (see below) uses this approach.
can I simply use MyCarReader<tblCarObj>
instead of
//for this instance.
private IEnumerator<T> enumerator = null;
//List of all public fields in <T>
private List<FieldInfo> fields = new List<FieldInfo>();
public MyGenericDataReader(IEnumerable<T> enumerator)
{
this.enumerator = enumerator.GetEnumerator();
//Find the enumerator of all public fields
foreach (FieldInfo fieldinfo in typeof(T).GetFields(
BindingFlags.Instance |
BindingFlags.Public))
{
fields.Add(fieldinfo);
}
}
is there a way to implement this method without the use of reflection ?
background
the task Is to simply upload a collection of objects into database table,
List<tblCarObj>
where:
DataBase Table is Ready
DataBase Table matches the DataObject members
both are known at compile time.
and directly use it on SQLBulckCopy instead of a more complex object such as DataTable
MyGenericDataReader.cs
GenericListDataReader.cs
You are asking the runtime and the compiler to do something that it cannot do without reflecting over the type.
The information that allows the CLR and the compiler to know what properties are on the type are in the type's metadata. Reflection is the mechanism that queries metadata and serves it up in a meaningful way so that we can use it to discover the shape and content of a type. Without it, a type is essentially a named black box.
You could do what you want, but you'd have to code it by hand. The reason the libraries out there exist is because reflection makes it possible for you to avoid that extra work.
needs further test to try
eliminate unnecessary mappings Update: tested ok
(gain extra performance when MyTestObject properties/fields are same as columns)
if making it generic will effect it only slightly the use it with T
using :
public string sqlCon ="data source=(local);Initial Catalog=XXX;Integrated Security=True";
public SqlCommand Cmd;
public SqlConnection Conn;
public SqlDataReader Drdr;
public Form1()
{
InitializeComponent();
this.Conn = new SqlConnection(this.sqlCon);
this.Cmd = new SqlCommand("select * from [tblTestBC]", this.Conn);
useBulkCopy();
}
void justread()
{
this.Cmd.Connection.Open();
this.Drdr = this.Cmd.ExecuteReader();
if (this.Drdr.HasRows)
while (this.Drdr.Read())
{
}
this.Cmd.Connection.Close();
}
void useBulkCopy()
{
var bulkCopy = new SqlBulkCopy(this.Cmd.Connection);
bulkCopy.DestinationTableName = "tblTestBC";
//bulkCopy.ColumnMappings.Add("age", "age");
//bulkCopy.ColumnMappings.Add("name", "name");
this.Cmd.Connection.Open();
try
{
using (var dataReader = new mySimpleDataReader())
{
bulkCopy.WriteToServer(dataReader);
}
this.Cmd.Connection.Close();
}
catch (Exception e)
{
}
}
GenericIdataReader
namespace GenericIdataReader
{
public class MyTestObject
{
public int age;
public string name;
}
public class mySimpleDataReader : IDataReader
{
private IEnumerator<MyTestObject> enumerator = null;
public List<MyTestObject> prpLst { get; set; }
List<MyTestObject> lst()
{
var rt = new List<MyTestObject>(5);
for (int i = 0; i < rt.Capacity; i++)
{
var tmp = new MyTestObject { age = i, name = "MyTestObject->"+i };
rt.Add(tmp);
}
return rt;
}
public mySimpleDataReader()
{
this.prpLst = this.lst();
this.enumerator = this.prpLst.GetEnumerator();
}
public void Close()
{
throw new NotImplementedException();
}
public int Depth
{
get { throw new NotImplementedException(); }
}
public DataTable GetSchemaTable()
{
throw new NotImplementedException();
}
public bool IsClosed
{
get { throw new NotImplementedException(); }
}
public bool NextResult()
{
throw new NotImplementedException();
}
public bool Read()
{
return enumerator.MoveNext();
}
public int RecordsAffected
{
get { throw new NotImplementedException(); }
}
public void Dispose()
{
this.enumerator.Dispose();
}
public int FieldCount
{
get { return 2; }// must be setted
}
public bool GetBoolean(int i)
{
throw new NotImplementedException();
}
public byte GetByte(int i)
{
throw new NotImplementedException();
}
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public char GetChar(int i)
{
throw new NotImplementedException();
}
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public IDataReader GetData(int i)
{
throw new NotImplementedException();
}
public string GetDataTypeName(int i)
{
throw new NotImplementedException();
}
public DateTime GetDateTime(int i)
{
throw new NotImplementedException();
}
public decimal GetDecimal(int i)
{
throw new NotImplementedException();
}
public double GetDouble(int i)
{
throw new NotImplementedException();
}
public Type GetFieldType(int i)
{
throw new NotImplementedException();
}
public float GetFloat(int i)
{
throw new NotImplementedException();
}
public Guid GetGuid(int i)
{
throw new NotImplementedException();
}
public short GetInt16(int i)
{
throw new NotImplementedException();
}
public int GetInt32(int i)
{
throw new NotImplementedException();
}
public long GetInt64(int i)
{
throw new NotImplementedException();
}
public string GetName(int i)
{
throw new NotImplementedException();
}
public int GetOrdinal(string name)
{
throw new NotImplementedException();
}
public string GetString(int i)
{
throw new NotImplementedException();
}
public object GetValue(int i) // this is where data is being pooled
{
if (i > 0) return enumerator.Current.name;
// so need to create an object that will hold numeric index or simply change
//this to return an indexed object instead of an enumerator according to parmeter i value
return enumerator.Current.age;
}
public int GetValues(object[] values)
{
throw new NotImplementedException();
}
public bool IsDBNull(int i)
{
throw new NotImplementedException();
}
public object this[string name]
{
get { throw new NotImplementedException(); }
}
public object this[int i]
{
get { throw new NotImplementedException(); }
}
}
}

Json.Net gives an error when creating my custom JsonConverter

I have a problem whereby I wish to generate a JSON field where the field name is known at runtime e.g.:
{ "known_at_run_time": ["test","test","test"] }
So I tried implementing it this way, yet whenever I run my unit test I get an error saying that that my custom JsonConverter cannot be created. Here is my code:
TermFilter.cs
public enum ExecutionType { plain, fielddata, #bool, and, or }
[JsonObject(MemberSerialization.OptIn)]
public class TermFilter
{
#region PROPERTIES
private JsonTuple query;
private ExecutionType execution;
private string _execution;
private bool _cache;
#endregion
#region CONSTRUCTOR
public TermFilter()
{
try
{
this.query = null;
this.Execution = ExecutionType.plain;
this.Cache = true;
}
catch(Exception)
{
throw;
}
}
public TermFilter(ExecutionType execution)
: this()
{
try
{
this.Execution = execution;
}
catch (Exception)
{
throw;
}
}
public TermFilter(ExecutionType execution, bool cache)
: this(execution)
{
try
{
this.Cache = cache;
}
catch (Exception)
{
throw;
}
}
public TermFilter(string field, string[] terms)
:this()
{
try
{
this.Query = new JsonTuple(field, new HashSet<string>(terms));
}
catch (Exception)
{
throw;
}
}
#endregion
#region GET/SET
//[JsonProperty(ItemConverterType = typeof(JsonTupleConverter))]
//[JsonProperty]
[JsonConverter( typeof(JsonTupleConverter) )]
public JsonTuple Query
{
get { return query; }
set { query = value; }
}
public ExecutionType Execution
{
get { return execution; }
set
{
execution = value;
_execution = value.ToString();
}
}
[JsonProperty(PropertyName = "execution")]
public string _Execution
{
get { return _execution; }
set { _execution = value; }
}
[JsonProperty(PropertyName = "_cache")]
public bool Cache
{
get { return _cache; }
set { _cache = value; }
}
#endregion
#region METHODS
public TermFilter AddTerm(string term)
{
try
{
if (!this.query.Data.Contains(term))
this.query.Data.Add(term);
return this;
}
catch (Exception)
{
throw;
}
}
public string ToJson()
{
try
{
var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;
settings.Converters.Add(new JsonTupleConverter(new Type[] { typeof(JsonTuple) }));
settings.NullValueHandling = NullValueHandling.Ignore;
return JsonConvert.SerializeObject(this, settings);
//return JsonConvert.SerializeObject(this, Formatting.None, new JsonTupleConverter(typeof(JsonTuple)));
//return JsonConvert.SerializeObject(this, Formatting.None, new JsonConverter[] { new JsonTupleConverter(typeof(JsonTuple)), });
}
catch (Exception)
{
throw;
}
}
#endregion
}
JsonTuple.cs
public class JsonTuple
{
#region PROPERTIES
private string field;
private HashSet<string> data;
#endregion
#region CONSTRUCTOR
public JsonTuple()
{
try
{
this.field = null;
this.data = null;
}
catch (Exception)
{
throw;
}
}
public JsonTuple(string field, HashSet<string> data)
{
try
{
this.field = field;
this.data = data;
}
catch (Exception)
{
throw;
}
}
#endregion
#region GET/SET
public string Field
{
get { return field; }
set { field = value; }
}
public HashSet<string> Data
{
get { return data; }
set { data = value; }
}
#endregion
#region METHODS
public string ToJson()
{
try
{
return JsonConvert.SerializeObject(this, Formatting.None, new JsonTupleConverter(typeof(JsonTuple)));
}
catch (Exception)
{
throw;
}
}
#endregion
}
JsonTupleConverter.cs
public class JsonTupleConverter : JsonConverter
{
private readonly Type[] _types;
public JsonTupleConverter(params Type[] types)
{
try
{
_types = types;
}
catch (Exception)
{
throw;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
try
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else if (!_types.Any(_t => _t == value.GetType()))
{
serializer.Serialize(writer, value);
}
else
{
JsonTuple tuple = (JsonTuple)value;
if ((tuple != null) && (tuple.Field != null) && (tuple.Data != null))
{
JToken entityToken = null;
if (tuple.Data != null)
entityToken = JToken.FromObject(tuple.Data);
JObject o = new JObject();
o.AddFirst(new JProperty(tuple.Field, entityToken));
o.WriteTo(writer);
}
}
}
catch (Exception)
{
throw;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return _types.Any(t => t == objectType);
}
}
Test.cs
[TestMethod]
public void TermFieldSertialization()
{
try
{
TermFilter filter = new TermFilter("test.field", new string[] {"test1", "test2", "test3"});
Assert.IsNotNull(filter);
string sampleJson = filter.ToJson();
Assert.IsNotNull(sampleJson);
}
catch (Exception)
{
throw;
}
}
What am I doing wrong? Any information will help.
First, try removing the [JsonConverter] attribute from the Query property of your TermFilter class. You don't need it because the Query property is a JsonTuple, and you are already passing an instance of your JsonTupleConverter to the JsonConvert.SerializeObject() method inside your ToJson() method, specifying that it can handle JsonTuples. This will get rid of the error.
However, there is still another issue. It seems that your intent is to get the Query property to serialize to JSON, but as things stand now this will not happen. This is because you have marked your TermFilter class with [JsonObject(MemberSerialization.OptIn)], and the Query property lacks a [JsonProperty] attribute to signal that you want that property to be included in the output. You will need to add [JsonProperty("query")] to fix that. Once you have done that, you should get the output you expect.
As an aside, you don't need to catch exceptions if you only intend to throw them again without doing anything else with them. I see that pattern everywhere in your code. Instead, just leave out the try/catch altogether; it does exactly the same thing and will make your code much more concise. Only catch an exception if you intend to handle it.
I think that your exception is occurring because JsonTupleConverter doesn't have a parameterless constructor.
public JsonTupleConverter() { }
If you add that, the error should go away, but your code might not work because then it'd probably be trying to use a converter without the types set up correctly.
Maybe you should just be serializing it as a dictionary? E.g.
var myDict = new Dictionary<string, List<string>>
{
{ "known_at_run_time", new List<string> { "test","test","test" } }
};
string ser = JsonConvert.SerializeObject(myDict);
// ser is {"known_at_run_time":["test","test","test"]}

Bind complex object to cell of DataTable in C#

I have a DataTable with complex objects.
For example,
class ComplexDataWrapper
{
public string Name{ get; set; }
public ComplexData Data{ get; set; }
public ComplexDataWrapper(ComplexData data)
{
this.Data = data;
this.Name = "Something";
}
public override string ToString()
{
return Name;
}
}
And now I want to bind cells from DataTable to objects of ComplexDataWrapper
So, I try something like this :
...
var column = new DataColumn() { ColumnName = columnName, DataType = typeof(ComplexDataWrapper)};
row[column] = new ComplexDataWrapper(data);
But, I want to bind for only one property, for example, Name.
And in the gridview (DataTable is a data source for this view) I want to edit this property(Name).
var complexDataWrapper = row[column] as ComplexDataWrapper;
complexDataWrapper always equals to NULL.
I know that I miss something.
So my questions : How I can bind my cell of DataTable to complex object? Plus in grid view I want to edit exactly one property of complex object.
Thanks. Hopefully, everything is clear.
So my questions : How I can bind my cell of DataTable to complex object? Plus in grid view I want to edit exactly one property of complex object.
What you need here is the ability to bind to a so called property path (e.g. obj.Prop1.Prop2). Unfortunately WinForms has limited support for that - it's supported for simple data binding (like control.DataBindings.Add(...)) but not for list data binding which is used by DataGridView control and similar.
Fortunately it's still doable with some (most of the time trivial) coding, because the data binding is build around an abstraction called PropertyDescriptor. By default it is implemented via reflection, but nothing prevents you to create your own implementation and do whatever you like inside it. That allows you to do many things that are not possible with reflection, in particular to simulate "properties" that actually do not exist.
Here we will utilize that possibility to create a "property" that actually gets/sets its value from a child property of the original property, while from outside it still looks like a single property, thus allowing to data bind to it:
public class ChildPropertyDescriptor : PropertyDescriptor
{
public static PropertyDescriptor Create(PropertyDescriptor sourceProperty, string childPropertyPath, string displayName = null)
{
var propertyNames = childPropertyPath.Split('.');
var propertyPath = new PropertyDescriptor[1 + propertyNames.Length];
propertyPath[0] = sourceProperty;
for (int i = 0; i < propertyNames.Length; i++)
propertyPath[i + 1] = propertyPath[i].GetChildProperties()[propertyNames[i]];
return new ChildPropertyDescriptor(propertyPath, displayName);
}
private ChildPropertyDescriptor(PropertyDescriptor[] propertyPath, string displayName)
: base(propertyPath[0].Name, null)
{
this.propertyPath = propertyPath;
this.displayName = displayName;
}
private PropertyDescriptor[] propertyPath;
private string displayName;
private PropertyDescriptor RootProperty { get { return propertyPath[0]; } }
private PropertyDescriptor ValueProperty { get { return propertyPath[propertyPath.Length - 1]; } }
public override Type ComponentType { get { return RootProperty.ComponentType; } }
public override bool IsReadOnly { get { return ValueProperty.IsReadOnly; } }
public override Type PropertyType { get { return ValueProperty.PropertyType; } }
public override bool CanResetValue(object component) { var target = GetTarget(component); return target != null && ValueProperty.CanResetValue(target); }
public override object GetValue(object component) { var target = GetTarget(component); return target != null ? ValueProperty.GetValue(target) : null; }
public override void ResetValue(object component) { ValueProperty.ResetValue(GetTarget(component)); }
public override void SetValue(object component, object value) { ValueProperty.SetValue(GetTarget(component), value); }
public override bool ShouldSerializeValue(object component) { var target = GetTarget(component); return target != null && ValueProperty.ShouldSerializeValue(target); }
public override AttributeCollection Attributes { get { return ValueProperty.Attributes; } }
public override string Category { get { return ValueProperty.Category; } }
public override TypeConverter Converter { get { return ValueProperty.Converter; } }
public override string Description { get { return ValueProperty.Description; } }
public override bool IsBrowsable { get { return ValueProperty.IsBrowsable; } }
public override bool IsLocalizable { get { return ValueProperty.IsLocalizable; } }
public override string DisplayName { get { return displayName ?? RootProperty.DisplayName; } }
public override object GetEditor(Type editorBaseType) { return ValueProperty.GetEditor(editorBaseType); }
public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter) { return ValueProperty.GetChildProperties(GetTarget(instance), filter); }
public override bool SupportsChangeEvents { get { return ValueProperty.SupportsChangeEvents; } }
public override void AddValueChanged(object component, EventHandler handler)
{
var target = GetTarget(component);
if (target != null)
ValueProperty.AddValueChanged(target, handler);
}
public override void RemoveValueChanged(object component, EventHandler handler)
{
var target = GetTarget(component);
if (target != null)
ValueProperty.RemoveValueChanged(target, handler);
}
private object GetTarget(object source)
{
var target = source;
for (int i = 0; target != null && target != DBNull.Value && i < propertyPath.Length - 1; i++)
target = propertyPath[i].GetValue(target);
return target != DBNull.Value ? target : null;
}
}
The code is not so small, but all it does is basically delegating the calls to the corresponding methods of the property descriptor chain representing the path from the original property to the child property. Also please note that many methods of the PropertyDescriptor are used only during the design time, so creating a custom concrete runtime property descriptor usually needs only to implement ComponentType, PropertyType, GetValue and SetValue (if supported).
So far so good. This is just the first part of the puzzle. We can create a "property", now we need a way to let data binding use it.
In order to do that, we'll utilize another data binding related interface called ITypedList:
Provides functionality to discover the schema for a bindable list, where the properties available for binding differ from the public properties of the object to bind to.
In other words, it allows us to provide "properties" for the list elements. But how? If we were implementing the data source list, it would be easy. But here we want to do that for a list that we don't know in advance (I'm trying the keep the solution generic).
The solutions is to wrap the original list in onother one that will implement IList (the minimum requirement for list data binding) by delegating all the calls to the underlying list, but by implementing ITypedList will control the properties used for binding:
public static class ListDataView
{
public static IList Create(object dataSource, string dataMember, Func<PropertyDescriptor, PropertyDescriptor> propertyMapper)
{
var source = (IList)ListBindingHelper.GetList(dataSource, dataMember);
if (source == null) return null;
if (source is IBindingListView) return new BindingListView((IBindingListView)source, propertyMapper);
if (source is IBindingList) return new BindingList((IBindingList)source, propertyMapper);
return new List(source, propertyMapper);
}
private class List : IList, ITypedList
{
private readonly IList source;
private readonly Func<PropertyDescriptor, PropertyDescriptor> propertyMapper;
public List(IList source, Func<PropertyDescriptor, PropertyDescriptor> propertyMapper) { this.source = source; this.propertyMapper = propertyMapper; }
// IList
public object this[int index] { get { return source[index]; } set { source[index] = value; } }
public int Count { get { return source.Count; } }
public bool IsFixedSize { get { return source.IsFixedSize; } }
public bool IsReadOnly { get { return source.IsReadOnly; } }
public bool IsSynchronized { get { return source.IsSynchronized; } }
public object SyncRoot { get { return source.SyncRoot; } }
public int Add(object value) { return source.Add(value); }
public void Clear() { source.Clear(); }
public bool Contains(object value) { return source.Contains(value); }
public void CopyTo(Array array, int index) { source.CopyTo(array, index); }
public IEnumerator GetEnumerator() { return source.GetEnumerator(); }
public int IndexOf(object value) { return source.IndexOf(value); }
public void Insert(int index, object value) { source.Insert(index, value); }
public void Remove(object value) { source.Remove(value); }
public void RemoveAt(int index) { source.RemoveAt(index); }
// ITypedList
public string GetListName(PropertyDescriptor[] listAccessors) { return ListBindingHelper.GetListName(source, listAccessors); }
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
var properties = ListBindingHelper.GetListItemProperties(source, listAccessors);
if (propertyMapper != null)
properties = new PropertyDescriptorCollection(properties.Cast<PropertyDescriptor>()
.Select(propertyMapper).Where(p => p != null).ToArray());
return properties;
}
}
private class BindingList : List, IBindingList
{
private IBindingList source;
public BindingList(IBindingList source, Func<PropertyDescriptor, PropertyDescriptor> propertyMapper) : base(source, propertyMapper) { this.source = source; }
private ListChangedEventHandler listChanged;
public event ListChangedEventHandler ListChanged
{
add
{
var oldHandler = listChanged;
if ((listChanged = oldHandler + value) != null && oldHandler == null)
source.ListChanged += OnListChanged;
}
remove
{
var oldHandler = listChanged;
if ((listChanged = oldHandler - value) == null && oldHandler != null)
source.ListChanged -= OnListChanged;
}
}
private void OnListChanged(object sender, ListChangedEventArgs e)
{
var handler = listChanged;
if (handler != null)
handler(this, e);
}
public bool AllowNew { get { return source.AllowNew; } }
public bool AllowEdit { get { return source.AllowEdit; } }
public bool AllowRemove { get { return source.AllowRemove; } }
public bool SupportsChangeNotification { get { return source.SupportsChangeNotification; } }
public bool SupportsSearching { get { return source.SupportsSearching; } }
public bool SupportsSorting { get { return source.SupportsSorting; } }
public bool IsSorted { get { return source.IsSorted; } }
public PropertyDescriptor SortProperty { get { return source.SortProperty; } }
public ListSortDirection SortDirection { get { return source.SortDirection; } }
public object AddNew() { return source.AddNew(); }
public void AddIndex(PropertyDescriptor property) { source.AddIndex(property); }
public void ApplySort(PropertyDescriptor property, ListSortDirection direction) { source.ApplySort(property, direction); }
public int Find(PropertyDescriptor property, object key) { return source.Find(property, key); }
public void RemoveIndex(PropertyDescriptor property) { source.RemoveIndex(property); }
public void RemoveSort() { source.RemoveSort(); }
}
private class BindingListView : BindingList, IBindingListView
{
private IBindingListView source;
public BindingListView(IBindingListView source, Func<PropertyDescriptor, PropertyDescriptor> propertyMapper) : base(source, propertyMapper) { this.source = source; }
public string Filter { get { return source.Filter; } set { source.Filter = value; } }
public ListSortDescriptionCollection SortDescriptions { get { return source.SortDescriptions; } }
public bool SupportsAdvancedSorting { get { return source.SupportsAdvancedSorting; } }
public bool SupportsFiltering { get { return source.SupportsFiltering; } }
public void ApplySort(ListSortDescriptionCollection sorts) { source.ApplySort(sorts); }
public void RemoveFilter() { source.RemoveFilter(); }
}
}
Actually as you can see, I've added wrappers for other data source interfaces like IBindingList and IBindingListView. Again, the code is not so small, but it's just delegating the calls to the underlying objects (when creating one for your concrete data, you usually would inherit from List<T> or BiundingList<T> and implement only the two ITypedList members). The essential part is the GetItemProperties method implementation which along with the propertyMapper lambda allows you to replace one property with another.
With all that in place, solving the specific problem from the post is simple a matter of wrapping the DataTable and mapping the Complex property to the Complex.Name property:
class ComplexData
{
public int Value { get; set; }
}
class ComplexDataWrapper
{
public string Name { get; set; }
public ComplexData Data { get; set; } = new ComplexData();
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form();
var gridView = new DataGridView { Dock = DockStyle.Fill, Parent = form };
gridView.DataSource = ListDataView.Create(GetData(), null, p =>
{
if (p.PropertyType == typeof(ComplexDataWrapper))
return ChildPropertyDescriptor.Create(p, "Name", "Complex Name");
return p;
});
Application.Run(form);
}
static DataTable GetData()
{
var dt = new DataTable();
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("Complex", typeof(ComplexDataWrapper));
for (int i = 1; i <= 10; i++)
dt.Rows.Add(i, new ComplexDataWrapper { Name = "Name#" + i, Data = new ComplexData { Value = i } });
return dt;
}
}
To recap, custom PropertyDescriptor and ITypedList allow you to create unlimited types of views of your data, which then can be used by any data bound aware control.
I believe your architecture is flawed for what you're trying to achieve.
If you are using the gridView to edit a single property on your complex type, then there is no need to bind the entire type into the datatable the is the datasource of your grid.
Instead you should bind only the property you wish to edit, and when the the data comes back, simply assign it to the complex type in the right place.

Object Duplication/Serialization problem

I'm having a bit of a problem with a data serialization method I'm using to copy objects. Here's the method:
public static class ObjectDuplicator
{
public static T Clone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("the Type must be serializable.", "source");
}
if (Object.ReferenceEquals(source, null)) //dont try to serialize a null object
{
return default(T);
}
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
}
The problem is this: when I call this method using the code below
public void AddJob(Job job)
{
if (!Jobs.Contains(job))
{
Job newcopy = Utilities.ObjectDuplicator.Clone<Job>(job);
Jobs.Add(newcopy);
}
}
it throws this exception:
System.InvalidCastException was unhandled
Message=Unable to cast object of type 'KH.CharacterClasses.Freelancer' to type 'KH.CharacterClasses.Job'
Now, the type of job I'm adding is an inherited class from Job, (Freelancer) and the code for those two classes is below
[Serializable]
public class Job : Ability
{
protected JobCommand basecommand1;
protected JobCommand basecommand2;
protected JobCommand basecommand3;
protected JobCommand basecommand4;
protected JobCommand command1;
protected JobCommand command2;
protected JobCommand command3;
protected JobCommand command4;
bool mastered;
protected FFJob job;
protected string name;
int level;
public FFJob SetJob
{
get
{
return job;
}
}
public bool Mastered
{
get
{
return mastered;
}
}
public JobCommand Command1
{
get
{
return command1;
}
set
{
command1 = value;
}
}
public JobCommand DefaultCommand1
{
get
{
return basecommand1;
}
}
public JobCommand Command2
{
get
{
return command2;
}
set
{
command2 = value;
}
}
public JobCommand DefaultCommand2
{
get
{
return basecommand2;
}
}
public JobCommand Command3
{
get
{
return command3;
}
set
{
command3 = value;
}
}
public JobCommand DefaultCommand3
{
get
{
return basecommand3;
}
}
public JobCommand Command4
{
get
{
return command4;
}
set
{
command4 = value;
}
}
public JobCommand DefaultCommand4
{
get
{
return basecommand4;
}
}
public Job(string name, string description, int jobID)
: base(name, description, jobID, -1, -1, null, null, -1, -1)
{
}
public static bool operator ==(Job job1, Job job2)
{
if (System.Object.ReferenceEquals(job1, job2))
return true;
if (((object)job1 == null) || ((object)job2 == null))
return false;
return (job1.Name == job2.Name && job1.UID == job2.UID);
}
public static bool operator !=(Job job1, Job job2)
{
return !(job1 == job2);
}
// public abstract void CharacterModifier(BaseCharacter character);
// public abstract void CharacterDemodifier(BaseCharacter character);
}
[Serializable]
public class Freelancer : Job
{
public Freelancer()
: base("Freelancer", "A character not specializing in any class. Can combine the power of all mastered Jobs.", Globals.JobID.ID)
{
basecommand1 = JobCommand.Attack;
basecommand2 = JobCommand.Free;
basecommand3 = JobCommand.Free;
basecommand4 = JobCommand.Items;
command1 = basecommand1;
command2 = basecommand2;
command3 = basecommand3;
command4 = basecommand4;
job = FFJob.Freelancer;
}
}
I'm a bit stumped here because I know the ObjectDuplicator method does work. In fact, this code HAS worked before, but that was on a different computer, and I haven't looked at it in awhile. I'm a little stumped as to why the casting fails here. If someone could help me out with whats wrong thatd be great. If you need more details, just say what you need. I asked this question yesterday, but didn't get a workable answer.
Thanks
Try replacing
return (T)formatter.Deserialize(stream);
with
var result = formatter.Deserialize(stream);
for (Type now = result.GetType(); now != null; now = now.BaseType)
MessageBox.Show(now.FullName);
return result as T;
What does Clone<T> return? Do you see KH.CharacterClasses.Job in a list of base types? Seems like it's not the base type for Freelancer.
I would never place a return statement into a using clause! Do this instead:
object tClone = null;
using (Stream stream = new MemoryStream()) {
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
tClone = formatter.Deserialize(stream);
}
return (T)tClone;
If the exception is still thrown, then your types are indeed incompatible...
Figured out the solution here:
Casting Error when using serialization

Categories

Resources