You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
7.7 KiB
215 lines
7.7 KiB
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Dapper
|
|
{
|
|
partial class SqlMapper
|
|
{
|
|
private sealed class DapperRow
|
|
: System.Dynamic.IDynamicMetaObjectProvider
|
|
, IDictionary<string, object>
|
|
{
|
|
readonly DapperTable table;
|
|
object[] values;
|
|
|
|
public DapperRow(DapperTable table, object[] values)
|
|
{
|
|
if (table == null) throw new ArgumentNullException(nameof(table));
|
|
if (values == null) throw new ArgumentNullException(nameof(values));
|
|
this.table = table;
|
|
this.values = values;
|
|
}
|
|
private sealed class DeadValue
|
|
{
|
|
public static readonly DeadValue Default = new DeadValue();
|
|
private DeadValue() { }
|
|
}
|
|
int ICollection<KeyValuePair<string, object>>.Count
|
|
{
|
|
get
|
|
{
|
|
int count = 0;
|
|
for (int i = 0; i < values.Length; i++)
|
|
{
|
|
if (!(values[i] is DeadValue)) count++;
|
|
}
|
|
return count;
|
|
}
|
|
}
|
|
|
|
public bool TryGetValue(string name, out object value)
|
|
{
|
|
var index = table.IndexOfName(name);
|
|
if (index < 0)
|
|
{ // doesn't exist
|
|
value = null;
|
|
return false;
|
|
}
|
|
// exists, **even if** we don't have a value; consider table rows heterogeneous
|
|
value = index < values.Length ? values[index] : null;
|
|
if (value is DeadValue)
|
|
{ // pretend it isn't here
|
|
value = null;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
var sb = GetStringBuilder().Append("{DapperRow");
|
|
foreach (var kv in this)
|
|
{
|
|
var value = kv.Value;
|
|
sb.Append(", ").Append(kv.Key);
|
|
if (value != null)
|
|
{
|
|
sb.Append(" = '").Append(kv.Value).Append('\'');
|
|
}
|
|
else
|
|
{
|
|
sb.Append(" = NULL");
|
|
}
|
|
}
|
|
|
|
return sb.Append('}').__ToStringRecycle();
|
|
}
|
|
|
|
System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(
|
|
System.Linq.Expressions.Expression parameter)
|
|
{
|
|
return new DapperRowMetaObject(parameter, System.Dynamic.BindingRestrictions.Empty, this);
|
|
}
|
|
|
|
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
|
{
|
|
var names = table.FieldNames;
|
|
for (var i = 0; i < names.Length; i++)
|
|
{
|
|
object value = i < values.Length ? values[i] : null;
|
|
if (!(value is DeadValue))
|
|
{
|
|
yield return new KeyValuePair<string, object>(names[i], value);
|
|
}
|
|
}
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
#region Implementation of ICollection<KeyValuePair<string,object>>
|
|
|
|
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
|
|
{
|
|
IDictionary<string, object> dic = this;
|
|
dic.Add(item.Key, item.Value);
|
|
}
|
|
|
|
void ICollection<KeyValuePair<string, object>>.Clear()
|
|
{ // removes values for **this row**, but doesn't change the fundamental table
|
|
for (int i = 0; i < values.Length; i++)
|
|
values[i] = DeadValue.Default;
|
|
}
|
|
|
|
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
|
|
{
|
|
object value;
|
|
return TryGetValue(item.Key, out value) && Equals(value, item.Value);
|
|
}
|
|
|
|
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
|
|
{
|
|
foreach (var kv in this)
|
|
{
|
|
array[arrayIndex++] = kv; // if they didn't leave enough space; not our fault
|
|
}
|
|
}
|
|
|
|
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
|
|
{
|
|
IDictionary<string, object> dic = this;
|
|
return dic.Remove(item.Key);
|
|
}
|
|
|
|
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
|
|
#endregion
|
|
|
|
#region Implementation of IDictionary<string,object>
|
|
|
|
bool IDictionary<string, object>.ContainsKey(string key)
|
|
{
|
|
int index = table.IndexOfName(key);
|
|
if (index < 0 || index >= values.Length || values[index] is DeadValue) return false;
|
|
return true;
|
|
}
|
|
|
|
void IDictionary<string, object>.Add(string key, object value)
|
|
{
|
|
SetValue(key, value, true);
|
|
}
|
|
|
|
bool IDictionary<string, object>.Remove(string key)
|
|
{
|
|
int index = table.IndexOfName(key);
|
|
if (index < 0 || index >= values.Length || values[index] is DeadValue) return false;
|
|
values[index] = DeadValue.Default;
|
|
return true;
|
|
}
|
|
|
|
object IDictionary<string, object>.this[string key]
|
|
{
|
|
get { object val; TryGetValue(key, out val); return val; }
|
|
set { SetValue(key, value, false); }
|
|
}
|
|
|
|
public object SetValue(string key, object value)
|
|
{
|
|
return SetValue(key, value, false);
|
|
}
|
|
|
|
private object SetValue(string key, object value, bool isAdd)
|
|
{
|
|
if (key == null) throw new ArgumentNullException(nameof(key));
|
|
int index = table.IndexOfName(key);
|
|
if (index < 0)
|
|
{
|
|
index = table.AddField(key);
|
|
}
|
|
else if (isAdd && index < values.Length && !(values[index] is DeadValue))
|
|
{
|
|
// then semantically, this value already exists
|
|
var tip = string.Format("key:{0}, index:{1} lenth:{2}", key, index, values.Length);
|
|
throw new ArgumentException("An item with the same key has already been added " + tip, nameof(key));
|
|
}
|
|
int oldLength = values.Length;
|
|
if (oldLength <= index)
|
|
{
|
|
// we'll assume they're doing lots of things, and
|
|
// grow it to the full width of the table
|
|
Array.Resize(ref values, table.FieldCount);
|
|
for (int i = oldLength; i < values.Length; i++)
|
|
{
|
|
values[i] = DeadValue.Default;
|
|
}
|
|
}
|
|
return values[index] = value;
|
|
}
|
|
|
|
ICollection<string> IDictionary<string, object>.Keys
|
|
{
|
|
get { return this.Select(kv => kv.Key).ToArray(); }
|
|
}
|
|
|
|
ICollection<object> IDictionary<string, object>.Values
|
|
{
|
|
get { return this.Select(kv => kv.Value).ToArray(); }
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
}
|
|
|