Skip to content

Commit

Permalink
feat: public interface IReadOnlyDataRow to handle wrappers around Dat…
Browse files Browse the repository at this point in the history
…aRow (#252)
  • Loading branch information
Seddryck authored Jan 7, 2024
1 parent 505eccb commit 48e892c
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 6 deletions.
98 changes: 96 additions & 2 deletions Expressif.Testing/ContextTest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Expressif.Values;
using Expressif.Values.Special;
using NUnit.Framework.Constraints;
using System.Data;

namespace Expressif.Testing;
Expand Down Expand Up @@ -220,7 +221,7 @@ public void CurrentObjectName_DictionaryWithUnavailableKey_ThrowsException()
}

[Test]
public void CurrentObjectName_ObjectWithUnavailableProperty_ThrowsException()
public void CurrentObjectName_AnonymousObjectWithUnavailableProperty_ThrowsException()
{
var context = new Context();
context.CurrentObject.Set(new { foo = 123 });
Expand All @@ -232,7 +233,38 @@ public void CurrentObjectName_ObjectWithUnavailableProperty_ThrowsException()
Assert.Multiple(() =>
{
Assert.That(() => context.CurrentObject["foo"], Throws.Nothing);
Assert.That(() => context.CurrentObject["bar"], Throws.TypeOf<ArgumentOutOfRangeException>());
Assert.That(() => context.CurrentObject["bar"], Throws.TypeOf<ArgumentException>()
.With.Message.EqualTo("Cannot find a property named 'bar' in the object of type '<>f__AnonymousType1`1'."));
});
}

private record ObjectTest(string Name) { }
[Test]
public void CurrentObjectName_ObjectWithUnavailableProperty_ThrowsException()
{
var context = new Context();
context.CurrentObject.Set(new ObjectTest("foo"));
Assert.That(context.CurrentObject.Contains("Bar"), Is.False);
Assert.Multiple(() =>
{
Assert.That(() => context.CurrentObject["Bar"], Throws.TypeOf<ArgumentException>()
.With.Message.EqualTo("Cannot find a property named 'Bar' in the object of type 'ObjectTest'."));
});
}

[Test]
public void CurrentObjectName_ObjectNull_ThrowsException()
{
var context = new Context();
context.CurrentObject.Set(null);
Assert.Multiple(() =>
{
Assert.That(context.CurrentObject.Contains("bar"), Is.False);
});
Assert.Multiple(() =>
{
Assert.That(() => context.CurrentObject["bar"], Throws.TypeOf<ArgumentException>()
.With.Message.EqualTo("Cannot find a property named 'bar' in the object of type 'null'."));
});
}

Expand Down Expand Up @@ -416,4 +448,66 @@ public void CurrentObjectValue_Any_CorrectResult()
context.CurrentObject.Set(new List<int>() { 123, 456 });
Assert.That(context.CurrentObject.Value, Is.AssignableTo<IList<int>>());
}

private class DataRowWrapper(DataRow row) : IReadOnlyDataRow
{
private DataRow Row { get; } = row;

public object? this[string columnName] => Row[columnName];

public object? this[int index] => Row[index];

public int ColumnsCount => Row.Table.Columns.Count;

public bool ContainsColumn(string columnName) => Row.Table.Columns.Contains(columnName);
}

[Test]
public void CurrentObjectIndex_IReadOnlyDataRowWithUnavailableColumn_ThrowsException()
{
var dt = new DataTable();
dt.Columns.Add("foo", typeof(int));
var row = dt.NewRow();
row.ItemArray = new object[] { 123 };
dt.Rows.Add(row);
var wrapper = new DataRowWrapper(row);

var context = new Context();
context.CurrentObject.Set(wrapper);
Assert.Multiple(() =>
{
Assert.That(context.CurrentObject.Contains(0), Is.True);
Assert.That(context.CurrentObject.Contains(1), Is.False);
});
Assert.Multiple(() =>
{
Assert.That(() => context.CurrentObject[0], Throws.Nothing);
Assert.That(() => context.CurrentObject[1], Throws.TypeOf<ArgumentOutOfRangeException>());
});
}

[Test]
public void CurrentObjectName_IReadOnlyDataRowWithExistingColumn_ValueReturned()
{
var dt = new DataTable();
dt.Columns.Add("foo", typeof(int));
dt.Columns.Add("bar", typeof(object));
var row = dt.NewRow();
row.ItemArray = new object[] { 123, 456 };
dt.Rows.Add(row);
var wrapper = new DataRowWrapper(row);

var context = new Context();
context.CurrentObject.Set(wrapper);
Assert.Multiple(() =>
{
Assert.That(context.CurrentObject.Contains("foo"), Is.True);
Assert.That(context.CurrentObject.Contains("bar"), Is.True);
});
Assert.Multiple(() =>
{
Assert.That(context.CurrentObject["foo"], Is.EqualTo(123));
Assert.That(context.CurrentObject["bar"], Is.EqualTo(456));
});
}
}
12 changes: 8 additions & 4 deletions Expressif/Values/ContextObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public class ContextObject
public object? Value { get; private set; }
private PropertyInfo[]? Cache { get; set; }

public void Set(object value)
public void Set(object? value)
{
if (Value == null || Value.GetType() != value.GetType())
if (value == null || Value == null || Value.GetType() != value.GetType())
Cache = null;
Value = value;
}
Expand All @@ -25,6 +25,7 @@ public bool Contains(string name)
=> Value switch
{
DataRow row => row.Table.Columns.Contains(name),
IReadOnlyDataRow row => row.ContainsColumn(name),
IDictionary dico => dico.Contains(name),
IList => throw new NotNameableContextObjectException(Value),
_ => TryRetrieveObjectProperty(name, out var _),
Expand All @@ -37,15 +38,16 @@ public object? this[string name]
return Value switch
{
DataRow row => row.Table.Columns.Contains(name) ? row[name] : throw new ArgumentOutOfRangeException(name),
IDictionary dico => dico.Contains(name) ? dico[name] : throw new ArgumentOutOfRangeException(name),
IReadOnlyDataRow row => row.ContainsColumn(name) ? row[name] : throw new ArgumentOutOfRangeException(name),
IDictionary dico => dico.Contains(name) ? dico[name] : throw new ArgumentOutOfRangeException(name),
IList => throw new NotNameableContextObjectException(Value),
_ => retrieveObjectProperty(name),
};

object? retrieveObjectProperty(string name)
=> TryRetrieveObjectProperty(name, out var value)
? value
: throw new ArgumentOutOfRangeException(name);
: throw new ArgumentException($"Cannot find a property named '{name}' in the object of type '{Value?.GetType().Name ?? "null"}'.");
}
}

Expand All @@ -60,6 +62,7 @@ public bool Contains(int index)
=> Value switch
{
DataRow row => index < row.Table.Columns.Count,
IReadOnlyDataRow row => index < row.ColumnsCount,
IList list => index < list.Count,
_ => throw new NotIndexableContextObjectException(Value)
};
Expand All @@ -71,6 +74,7 @@ public object? this[int index]
return Value switch
{
DataRow row => index < row.Table.Columns.Count ? row[index] : throw new ArgumentOutOfRangeException(index.ToString()),
IReadOnlyDataRow row => index < row.ColumnsCount ? row[index] : throw new ArgumentOutOfRangeException(index.ToString()),
IList list => list[index],
_ => throw new NotIndexableContextObjectException(Value)
};
Expand Down
15 changes: 15 additions & 0 deletions Expressif/Values/IReadOnlyDataRow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace Expressif.Values;
public interface IReadOnlyDataRow
{
int ColumnsCount { get; }
bool ContainsColumn(string columnName);
object? this[string columnName] { get; }
object? this[int index] { get; }
}

0 comments on commit 48e892c

Please sign in to comment.