Skip to content

Commit

Permalink
Added RecordSet.FromModel methods - create RecordSet by annotated mod…
Browse files Browse the repository at this point in the history
…el(s) #17
  • Loading branch information
VitaliyMF committed Sep 18, 2016
1 parent e38d1a4 commit 089ae0c
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 4 deletions.
50 changes: 50 additions & 0 deletions src/NReco.Data.Tests/RecordSetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

using Xunit;
using NReco.Data;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace NReco.Data.Tests
{
Expand Down Expand Up @@ -144,5 +146,53 @@ public void RecordSetReader() {
Assert.Equal("Name99", newRS[99].Field<string>("name"));
}

[Fact]
public void RecordSet_FromModel() {

var rs1 = RecordSet.FromModel<PersonModel>();

Assert.Equal( 5, rs1.Columns.Count );
Assert.Equal( 1, rs1.PrimaryKey.Length );
Assert.Equal("id", rs1.PrimaryKey[0].Name );
Assert.True(rs1.PrimaryKey[0].AutoIncrement );
Assert.True(rs1.PrimaryKey[0].ReadOnly );

Assert.Equal("first_name", rs1.Columns[1].Name );

var rs2 = RecordSet.FromModel( new PersonModel() { Id = 9, FirstName = "John" }, RecordSet.RowState.Modified );
Assert.Equal(1, rs2.Count);
Assert.Equal(9, rs2[0].Field<int>("id") );
Assert.Equal("John", rs2[0].Field<string>("first_name") );
Assert.Equal(RecordSet.RowState.Modified, rs2[0].State );
}


[Table("persons")]
public class PersonModel {

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[Column("id")]
public int? Id { get; set; }

[Column("first_name")]
public string FirstName { get; set; }

[Column("last_name")]
public string LastName { get; set; }

[Column("birthday")]
public DateTime? BirthDay { get; set; }

// not annotated field
public bool Active;

[NotMapped]
public string Name {
get { return $"{FirstName} {LastName}"; }
}
}


}
}
9 changes: 6 additions & 3 deletions src/NReco.Data/DataMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ PocoModelSchema InferSchema(Type t) {
var colMapping = new ColumnMapping(
metadata.Item1 ?? prop.Name, t, prop.Name, prop.PropertyType,
prop.CanRead, prop.CanWrite,
metadata.Item4, metadata.Item5);
metadata.Item4, metadata.Item5, metadata.Item2);
if (metadata.Item2) // is key
keyCols.Add(colMapping);
cols.Add(colMapping);
Expand All @@ -62,7 +62,7 @@ PocoModelSchema InferSchema(Type t) {
var colMapping = new ColumnMapping(
metadata.Item1 ?? fld.Name, t, fld.Name, fld.FieldType,
true, true,
metadata.Item4, metadata.Item5);
metadata.Item4, metadata.Item5, metadata.Item2);
if (metadata.Item2) // is key
keyCols.Add(colMapping);
cols.Add(colMapping);
Expand Down Expand Up @@ -167,15 +167,18 @@ internal class ColumnMapping {

internal readonly bool IsIdentity;

internal readonly bool IsKey;

internal ColumnMapping(
string colName, Type t,
string propOrFieldName, Type propOrFieldType,
bool canRead, bool canWrite,
bool isReadOnly, bool isIdentity) {
bool isReadOnly, bool isIdentity, bool isKey) {
ColumnName = colName;
ValueType = propOrFieldType;
IsReadOnly = isReadOnly;
IsIdentity = isIdentity;
IsKey = isKey;

// compose get
if (canRead) {
Expand Down
60 changes: 59 additions & 1 deletion src/NReco.Data/RecordSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,64 @@ public static RecordSet FromReader(IDataReader rdr) {
return rs;
}

/// <summary>
/// Creates an empty <see cref="RecordSet"/> with schema inferred from the annotated object model.
/// </summary>
/// <typeparam name="T">annotated model type</typeparam>
/// <returns>empty <see cref="RecordSet"/></returns>
public static RecordSet FromModel<T>() {
return RecordSet.FromModel<T>(null, RowState.Added);
}

/// <summary>
/// Creates a <see cref="RecordSet"/> with schema and row inferred from the annotated object model.
/// </summary>
/// <typeparam name="T">annotated model type</typeparam>
/// <param name="model">model instance</param>
/// <param name="rowState">intial state of row created by model</param>
/// <returns><see cref="RecordSet"/> with one row</returns>
public static RecordSet FromModel<T>(T model, RowState rowState) {
return RecordSet.FromModel<T>(new[] {model}, rowState);
}

/// <summary>
/// Creates a <see cref="RecordSet"/> with schema and rows inferred from the annotated object models.
/// </summary>
/// <typeparam name="T">annotated model type</typeparam>
/// <param name="models">sequence of models</param>
/// <param name="rowState">intial state of rows created by models</param>
/// <returns><see cref="RecordSet"/> with rows</returns>
public static RecordSet FromModel<T>(IEnumerable<T> models, RowState rowState) {
var schema = DataMapper.Instance.GetSchema(typeof(T));
if (schema.Columns.Length==0)
throw new ArgumentException($"Model of type {typeof(T).Name} has no columns");
var rsCols = new Column[schema.Columns.Length];
var pkCols = new List<Column>(schema.Key.Length);
for (int i=0; i<rsCols.Length; i++) {
var modelCol = schema.Columns[i];
rsCols[i] = new Column(modelCol.ColumnName, modelCol.ValueType) {
AutoIncrement = modelCol.IsIdentity,
ReadOnly = modelCol.IsReadOnly
};
if (modelCol.IsKey)
pkCols.Add(rsCols[i]);
}
var rs = new RecordSet(rsCols);
rs.PrimaryKey = pkCols.ToArray();
if (models!=null) {
foreach (var dto in models) {
var rowData = new object[schema.Columns.Length];
for (int i=0; i<schema.Columns.Length; i++) {
var modelCol = schema.Columns[i];
if (modelCol.GetVal!=null)
rowData[i] = modelCol.GetVal(dto);
}
rs.Add(rowData).rowState = rowState;
}
}
return rs;
}

/// <summary>
/// Asynchronously creates a <see cref="RecordSet"/> from a data source using the supplied <see cref="IDataReader"/>.
/// </summary>
Expand Down Expand Up @@ -341,7 +399,7 @@ public RowState State {
return rowState;
}
}
RowState rowState;
internal RowState rowState;

RecordSet rs;
object[] values;
Expand Down

0 comments on commit 089ae0c

Please sign in to comment.