added marr.datamapper source code for easy debugging.

This commit is contained in:
kay.one 2013-03-30 14:56:34 -07:00
commit 3cdff3bb71
96 changed files with 9198 additions and 363 deletions

View file

@ -0,0 +1,122 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.OleDb;
using System.Data.Common;
namespace Marr.Data.Mapping
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class ColumnAttribute : Attribute, IColumnInfo
{
private string _name;
private string _altName;
private int _size = 0;
private bool _isPrimaryKey;
private bool _isAutoIncrement;
private bool _returnValue;
private ParameterDirection _paramDirection = ParameterDirection.Input;
public ColumnAttribute()
{
}
public ColumnAttribute(string name)
{
_name = name;
}
/// <summary>
/// Gets or sets the column name.
/// </summary>
public string Name
{
get { return _name; }
set { _name = value; }
}
/// <summary>
/// Gets or sets an alternate name that is used to define this column in views.
/// If an AltName is present, it is used in the QueryViewToObjectGraph method.
/// If an AltName is not present, it will return the Name property value.
/// </summary>
public string AltName
{
get { return _altName; }
set { _altName = value; }
}
/// <summary>
/// Gets or sets the column size.
/// </summary>
public int Size
{
get { return _size; }
set { _size = value; }
}
/// <summary>
/// Gets or sets a value that determines whether the column is the Primary Key.
/// </summary>
public bool IsPrimaryKey
{
get { return _isPrimaryKey; }
set { _isPrimaryKey = value; }
}
/// <summary>
/// Gets or sets a value that determines whether the column is an auto-incrementing seed column.
/// </summary>
public bool IsAutoIncrement
{
get { return _isAutoIncrement; }
set { _isAutoIncrement = value; }
}
/// <summary>
/// Gets or sets a value that determines whether the column has a return value.
/// </summary>
public bool ReturnValue
{
get { return _returnValue; }
set { _returnValue = value; }
}
/// <summary>
/// Gets or sets the ParameterDirection.
/// </summary>
public ParameterDirection ParamDirection
{
get { return _paramDirection; }
set { _paramDirection = value; }
}
public string TryGetAltName()
{
if (!string.IsNullOrEmpty(AltName) && AltName != Name)
{
return AltName;
}
else
{
return Name;
}
}
}
}

View file

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Marr.Data.Mapping
{
public class ColumnInfo : IColumnInfo
{
public ColumnInfo()
{
IsPrimaryKey = false;
IsAutoIncrement = false;
ReturnValue = false;
ParamDirection = System.Data.ParameterDirection.Input;
}
public string Name { get; set; }
public string AltName { get; set; }
public int Size { get; set; }
public bool IsPrimaryKey { get; set; }
public bool IsAutoIncrement { get; set; }
public bool ReturnValue { get; set; }
public System.Data.ParameterDirection ParamDirection { get; set; }
public string TryGetAltName()
{
if (!string.IsNullOrEmpty(AltName) && AltName != Name)
{
return AltName;
}
else
{
return Name;
}
}
}
}

View file

@ -0,0 +1,69 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Reflection;
using Marr.Data.Converters;
namespace Marr.Data.Mapping
{
/// <summary>
/// Contains information about the class fields and their associated stored proc parameters
/// </summary>
public class ColumnMap
{
/// <summary>
/// Creates a column map with an empty ColumnInfo object.
/// </summary>
/// <param name="member">The .net member that is being mapped.</param>
public ColumnMap(MemberInfo member)
: this(member, new ColumnInfo())
{ }
public ColumnMap(MemberInfo member, IColumnInfo columnInfo)
{
FieldName = member.Name;
// If the column name is not specified, the field name will be used.
if (string.IsNullOrEmpty(columnInfo.Name))
columnInfo.Name = member.Name;
FieldType = ReflectionHelper.GetMemberType(member);
Type paramNetType = FieldType;
MapRepository repository = MapRepository.Instance;
IConverter converter = repository.GetConverter(FieldType);
if (converter != null)
{
// Handle conversions
paramNetType = converter.DbType;
}
// Get database specific DbType and store with column map in cache
DBType = repository.DbTypeBuilder.GetDbType(paramNetType);
ColumnInfo = columnInfo;
}
public string FieldName { get; set; }
public Type FieldType { get; set; }
public Enum DBType { get; set; }
public IColumnInfo ColumnInfo { get; set; }
}
}

View file

@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Data;
using Marr.Data.Mapping.Strategies;
namespace Marr.Data.Mapping
{
/// <summary>
/// This class has fluent methods that are used to easily configure column mappings.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ColumnMapBuilder<TEntity>
{
private FluentMappings.MappingsFluentEntity<TEntity> _fluentEntity;
private string _currentPropertyName;
public ColumnMapBuilder(FluentMappings.MappingsFluentEntity<TEntity> fluentEntity, ColumnMapCollection mappedColumns)
{
_fluentEntity = fluentEntity;
MappedColumns = mappedColumns;
}
/// <summary>
/// Gets the list of column mappings that are being configured.
/// </summary>
public ColumnMapCollection MappedColumns { get; private set; }
#region - Fluent Methods -
/// <summary>
/// Initializes the configurator to configure the given property.
/// </summary>
/// <param name="property"></param>
/// <returns></returns>
public ColumnMapBuilder<TEntity> For(Expression<Func<TEntity, object>> property)
{
For(property.GetMemberName());
return this;
}
/// <summary>
/// Initializes the configurator to configure the given property or field.
/// </summary>
/// <param name="property"></param>
/// <returns></returns>
public ColumnMapBuilder<TEntity> For(string propertyName)
{
_currentPropertyName = propertyName;
// Try to add the column map if it doesn't exist
if (MappedColumns.GetByFieldName(_currentPropertyName) == null)
{
TryAddColumnMapForField(_currentPropertyName);
}
return this;
}
public ColumnMapBuilder<TEntity> SetPrimaryKey()
{
AssertCurrentPropertyIsSet();
return SetPrimaryKey(_currentPropertyName);
}
public ColumnMapBuilder<TEntity> SetPrimaryKey(string propertyName)
{
MappedColumns.GetByFieldName(propertyName).ColumnInfo.IsPrimaryKey = true;
return this;
}
public ColumnMapBuilder<TEntity> SetAutoIncrement()
{
AssertCurrentPropertyIsSet();
return SetAutoIncrement(_currentPropertyName);
}
public ColumnMapBuilder<TEntity> SetAutoIncrement(string propertyName)
{
MappedColumns.GetByFieldName(propertyName).ColumnInfo.IsAutoIncrement = true;
return this;
}
public ColumnMapBuilder<TEntity> SetColumnName(string columnName)
{
AssertCurrentPropertyIsSet();
return SetColumnName(_currentPropertyName, columnName);
}
public ColumnMapBuilder<TEntity> SetColumnName(string propertyName, string columnName)
{
MappedColumns.GetByFieldName(propertyName).ColumnInfo.Name = columnName;
return this;
}
public ColumnMapBuilder<TEntity> SetReturnValue()
{
AssertCurrentPropertyIsSet();
return SetReturnValue(_currentPropertyName);
}
public ColumnMapBuilder<TEntity> SetReturnValue(string propertyName)
{
MappedColumns.GetByFieldName(propertyName).ColumnInfo.ReturnValue = true;
return this;
}
public ColumnMapBuilder<TEntity> SetSize(int size)
{
AssertCurrentPropertyIsSet();
return SetSize(_currentPropertyName, size);
}
public ColumnMapBuilder<TEntity> SetSize(string propertyName, int size)
{
MappedColumns.GetByFieldName(propertyName).ColumnInfo.Size = size;
return this;
}
public ColumnMapBuilder<TEntity> SetAltName(string altName)
{
AssertCurrentPropertyIsSet();
return SetAltName(_currentPropertyName, altName);
}
public ColumnMapBuilder<TEntity> SetAltName(string propertyName, string altName)
{
MappedColumns.GetByFieldName(propertyName).ColumnInfo.AltName = altName;
return this;
}
public ColumnMapBuilder<TEntity> SetParamDirection(ParameterDirection direction)
{
AssertCurrentPropertyIsSet();
return SetParamDirection(_currentPropertyName, direction);
}
public ColumnMapBuilder<TEntity> SetParamDirection(string propertyName, ParameterDirection direction)
{
MappedColumns.GetByFieldName(propertyName).ColumnInfo.ParamDirection = direction;
return this;
}
public ColumnMapBuilder<TEntity> Ignore(Expression<Func<TEntity, object>> property)
{
string propertyName = property.GetMemberName();
return Ignore(propertyName);
}
public ColumnMapBuilder<TEntity> Ignore(string propertyName)
{
var columnMap = MappedColumns.GetByFieldName(propertyName);
MappedColumns.Remove(columnMap);
return this;
}
public ColumnMapBuilder<TEntity> PrefixAltNames(string prefix)
{
MappedColumns.PrefixAltNames(prefix);
return this;
}
public ColumnMapBuilder<TEntity> SuffixAltNames(string suffix)
{
MappedColumns.SuffixAltNames(suffix);
return this;
}
public FluentMappings.MappingsFluentTables<TEntity> Tables
{
get
{
if (_fluentEntity == null)
{
throw new Exception("This property is not compatible with the obsolete 'MapBuilder' class.");
}
return _fluentEntity.Table;
}
}
public FluentMappings.MappingsFluentRelationships<TEntity> Relationships
{
get
{
if (_fluentEntity == null)
{
throw new Exception("This property is not compatible with the obsolete 'MapBuilder' class.");
}
return _fluentEntity.Relationships;
}
}
public FluentMappings.MappingsFluentEntity<TNewEntity> Entity<TNewEntity>()
{
return new FluentMappings.MappingsFluentEntity<TNewEntity>(true);
}
/// <summary>
/// Tries to add a ColumnMap for the given field name.
/// Throws and exception if field cannot be found.
/// </summary>
private void TryAddColumnMapForField(string fieldName)
{
// Set strategy to filter for public or private fields
ConventionMapStrategy strategy = new ConventionMapStrategy(false);
// Find the field that matches the given field name
strategy.ColumnPredicate = mi => mi.Name == fieldName;
ColumnMap columnMap = strategy.MapColumns(typeof(TEntity)).FirstOrDefault();
if (columnMap == null)
{
throw new DataMappingException(string.Format("Could not find the field '{0}' in '{1}'.",
fieldName,
typeof(TEntity).Name));
}
else
{
MappedColumns.Add(columnMap);
}
}
/// <summary>
/// Throws an exception if the "current" property has not been set.
/// </summary>
private void AssertCurrentPropertyIsSet()
{
if (string.IsNullOrEmpty(_currentPropertyName))
{
throw new DataMappingException("A property must first be specified using the 'For' method.");
}
}
#endregion
}
}

View file

@ -0,0 +1,176 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Text.RegularExpressions;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
namespace Marr.Data.Mapping
{
/// <summary>
/// This class contains a list of column mappings.
/// It also provides various methods to filter the collection.
/// </summary>
public class ColumnMapCollection : List<ColumnMap>
{
#region - Filters -
public ColumnMap GetByColumnName(string columnName)
{
return this.Find(m => m.ColumnInfo.Name == columnName);
}
public ColumnMap GetByFieldName(string fieldName)
{
return this.Find(m => m.FieldName == fieldName);
}
/// <summary>
/// Iterates through all fields marked as return values.
/// </summary>
public IEnumerable<ColumnMap> ReturnValues
{
get
{
foreach (ColumnMap map in this)
if (map.ColumnInfo.ReturnValue)
yield return map;
}
}
/// <summary>
/// Iterates through all fields that are not return values.
/// </summary>
public ColumnMapCollection NonReturnValues
{
get
{
ColumnMapCollection collection = new ColumnMapCollection();
foreach (ColumnMap map in this)
if (!map.ColumnInfo.ReturnValue)
collection.Add(map);
return collection;
}
}
/// <summary>
/// Iterates through all fields marked as Output parameters or InputOutput.
/// </summary>
public IEnumerable<ColumnMap> OutputFields
{
get
{
foreach (ColumnMap map in this)
if (map.ColumnInfo.ParamDirection == ParameterDirection.InputOutput ||
map.ColumnInfo.ParamDirection == ParameterDirection.Output)
yield return map;
}
}
/// <summary>
/// Iterates through all fields marked as primary keys.
/// </summary>
public ColumnMapCollection PrimaryKeys
{
get
{
ColumnMapCollection keys = new ColumnMapCollection();
foreach (ColumnMap map in this)
if (map.ColumnInfo.IsPrimaryKey)
keys.Add(map);
return keys;
}
}
/// <summary>
/// Parses and orders the parameters from the query text.
/// Filters the list of mapped columns to match the parameters found in the sql query.
/// All parameters starting with the '@' or ':' symbol are matched and returned.
/// </summary>
/// <param name="command">The command and parameters that are being parsed.</param>
/// <returns>A list of mapped columns that are present in the sql statement as parameters.</returns>
public ColumnMapCollection OrderParameters(DbCommand command)
{
if (command.CommandType == CommandType.Text && this.Count > 0)
{
string commandTypeString = command.GetType().ToString();
if (commandTypeString.Contains("Oracle") || commandTypeString.Contains("OleDb"))
{
ColumnMapCollection columns = new ColumnMapCollection();
// Find all @Parameters contained in the sql statement
string paramPrefix = commandTypeString.Contains("Oracle") ? ":" : "@";
string regexString = string.Format(@"{0}[\w-]+", paramPrefix);
Regex regex = new Regex(regexString);
foreach (Match m in regex.Matches(command.CommandText))
{
ColumnMap matchingColumn = this.Find(c => string.Concat(paramPrefix, c.ColumnInfo.Name.ToLower()) == m.Value.ToLower());
if (matchingColumn != null)
columns.Add(matchingColumn);
}
return columns;
}
}
return this;
}
#endregion
#region - Actions -
/// <summary>
/// Set's each column's altname as the given prefix + the column name.
/// Ex:
/// Original column name: "ID"
/// Passed in prefix: "PRODUCT_"
/// Generated AltName: "PRODUCT_ID"
/// </summary>
/// <param name="prefix">The given prefix.</param>
/// <returns></returns>
public ColumnMapCollection PrefixAltNames(string prefix)
{
this.ForEach(c => c.ColumnInfo.AltName = c.ColumnInfo.Name.Insert(0, prefix));
return this;
}
/// <summary>
/// Set's each column's altname as the column name + the given prefix.
/// Ex:
/// Original column name: "ID"
/// Passed in suffix: "_PRODUCT"
/// Generated AltName: "ID_PRODUCT"
/// </summary>
/// <param name="suffix"></param>
/// <returns></returns>
public ColumnMapCollection SuffixAltNames(string suffix)
{
this.ForEach(c => c.ColumnInfo.AltName = c.ColumnInfo.Name + suffix);
return this;
}
#endregion
}
}

View file

@ -0,0 +1,29 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Marr.Data.Mapping
{
public enum EnumConversionType
{
NA,
Int,
String
}
}

View file

@ -0,0 +1,236 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Marr.Data.Mapping.Strategies;
using System.Collections;
namespace Marr.Data.Mapping
{
/// <summary>
/// Provides a fluent interface for mapping domain entities and properties to database tables and columns.
/// </summary>
public class FluentMappings
{
private bool _publicOnly;
public FluentMappings()
: this(true)
{ }
public FluentMappings(bool publicOnly)
{
_publicOnly = publicOnly;
}
public MappingsFluentEntity<TEntity> Entity<TEntity>()
{
return new MappingsFluentEntity<TEntity>(_publicOnly);
}
public class MappingsFluentEntity<TEntity>
{
public MappingsFluentEntity(bool publicOnly)
{
Columns = new MappingsFluentColumns<TEntity>(this, publicOnly);
Table = new MappingsFluentTables<TEntity>(this);
Relationships = new MappingsFluentRelationships<TEntity>(this, publicOnly);
}
/// <summary>
/// Contains methods that map entity properties to database table and view column names;
/// </summary>
public MappingsFluentColumns<TEntity> Columns { get; private set; }
/// <summary>
/// Contains methods that map entity classes to database table names.
/// </summary>
public MappingsFluentTables<TEntity> Table { get; private set; }
/// <summary>
/// Contains methods that map sub-entities with database table and view column names.
/// </summary>
public MappingsFluentRelationships<TEntity> Relationships { get; private set; }
}
public class MappingsFluentColumns<TEntity>
{
private bool _publicOnly;
private FluentMappings.MappingsFluentEntity<TEntity> _fluentEntity;
public MappingsFluentColumns(FluentMappings.MappingsFluentEntity<TEntity> fluentEntity, bool publicOnly)
{
_fluentEntity = fluentEntity;
_publicOnly = publicOnly;
}
/// <summary>
/// Creates column mappings for the given type.
/// Maps all properties except ICollection properties.
/// </summary>
/// <typeparam name="T">The type that is being built.</typeparam>
/// <returns><see cref="ColumnMapCollection"/></returns>
public ColumnMapBuilder<TEntity> AutoMapAllProperties()
{
return AutoMapPropertiesWhere(m => m.MemberType == MemberTypes.Property &&
!typeof(ICollection).IsAssignableFrom((m as PropertyInfo).PropertyType));
}
/// <summary>
/// Creates column mappings for the given type.
/// Maps all properties that are simple types (int, string, DateTime, etc).
/// ICollection properties are not included.
/// </summary>
/// <typeparam name="T">The type that is being built.</typeparam>
/// <returns><see cref="ColumnMapCollection"/></returns>
public ColumnMapBuilder<TEntity> AutoMapSimpleTypeProperties()
{
return AutoMapPropertiesWhere(m => m.MemberType == MemberTypes.Property &&
DataHelper.IsSimpleType((m as PropertyInfo).PropertyType) &&
!typeof(ICollection).IsAssignableFrom((m as PropertyInfo).PropertyType));
}
/// <summary>
/// Creates column mappings for the given type if they match the predicate.
/// </summary>
/// <typeparam name="T">The type that is being built.</typeparam>
/// <param name="predicate">Determines whether a mapping should be created based on the member info.</param>
/// <returns><see cref="ColumnMapConfigurator"/></returns>
public ColumnMapBuilder<TEntity> AutoMapPropertiesWhere(Func<MemberInfo, bool> predicate)
{
Type entityType = typeof(TEntity);
ConventionMapStrategy strategy = new ConventionMapStrategy(_publicOnly);
strategy.ColumnPredicate = predicate;
ColumnMapCollection columns = strategy.MapColumns(entityType);
MapRepository.Instance.Columns[entityType] = columns;
return new ColumnMapBuilder<TEntity>(_fluentEntity, columns);
}
/// <summary>
/// Creates a ColumnMapBuilder that starts out with no pre-populated columns.
/// All columns must be added manually using the builder.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public ColumnMapBuilder<TEntity> MapProperties()
{
Type entityType = typeof(TEntity);
ColumnMapCollection columns = new ColumnMapCollection();
MapRepository.Instance.Columns[entityType] = columns;
return new ColumnMapBuilder<TEntity>(_fluentEntity, columns);
}
}
public class MappingsFluentTables<TEntity>
{
private FluentMappings.MappingsFluentEntity<TEntity> _fluentEntity;
public MappingsFluentTables(FluentMappings.MappingsFluentEntity<TEntity> fluentEntity)
{
_fluentEntity = fluentEntity;
}
/// <summary>
/// Provides a fluent table mapping interface.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public TableBuilder<TEntity> AutoMapTable<T>()
{
return new TableBuilder<TEntity>(_fluentEntity);
}
/// <summary>
/// Sets the table name for a given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tableName"></param>
public TableBuilder<TEntity> MapTable(string tableName)
{
return new TableBuilder<TEntity>(_fluentEntity).SetTableName(tableName);
}
}
public class MappingsFluentRelationships<TEntity>
{
private FluentMappings.MappingsFluentEntity<TEntity> _fluentEntity;
private bool _publicOnly;
public MappingsFluentRelationships(FluentMappings.MappingsFluentEntity<TEntity> fluentEntity, bool publicOnly)
{
_fluentEntity = fluentEntity;
_publicOnly = publicOnly;
}
/// <summary>
/// Creates relationship mappings for the given type.
/// Maps all properties that implement ICollection or are not "simple types".
/// </summary>
/// <returns></returns>
public RelationshipBuilder<TEntity> AutoMapICollectionOrComplexProperties()
{
return AutoMapPropertiesWhere(m =>
m.MemberType == MemberTypes.Property &&
(
typeof(ICollection).IsAssignableFrom((m as PropertyInfo).PropertyType) || !DataHelper.IsSimpleType((m as PropertyInfo).PropertyType)
)
);
}
/// <summary>
/// Creates relationship mappings for the given type.
/// Maps all properties that implement ICollection.
/// </summary>
/// <returns><see cref="RelationshipBuilder"/></returns>
public RelationshipBuilder<TEntity> AutoMapICollectionProperties()
{
return AutoMapPropertiesWhere(m =>
m.MemberType == MemberTypes.Property &&
typeof(ICollection).IsAssignableFrom((m as PropertyInfo).PropertyType));
}
/// <summary>
/// Creates relationship mappings for the given type.
/// Maps all properties that are not "simple types".
/// </summary>
/// <returns></returns>
public RelationshipBuilder<TEntity> AutoMapComplexTypeProperties<T>()
{
return AutoMapPropertiesWhere(m =>
m.MemberType == MemberTypes.Property &&
!DataHelper.IsSimpleType((m as PropertyInfo).PropertyType));
}
/// <summary>
/// Creates relationship mappings for the given type if they match the predicate.
/// </summary>
/// <param name="predicate">Determines whether a mapping should be created based on the member info.</param>
/// <returns><see cref="RelationshipBuilder"/></returns>
public RelationshipBuilder<TEntity> AutoMapPropertiesWhere(Func<MemberInfo, bool> predicate)
{
Type entityType = typeof(TEntity);
ConventionMapStrategy strategy = new ConventionMapStrategy(_publicOnly);
strategy.RelationshipPredicate = predicate;
RelationshipCollection relationships = strategy.MapRelationships(entityType);
MapRepository.Instance.Relationships[entityType] = relationships;
return new RelationshipBuilder<TEntity>(_fluentEntity, relationships);
}
/// <summary>
/// Creates a RelationshipBuilder that starts out with no pre-populated relationships.
/// All relationships must be added manually using the builder.
/// </summary>
/// <returns></returns>
public RelationshipBuilder<TEntity> MapProperties<T>()
{
Type entityType = typeof(T);
RelationshipCollection relationships = new RelationshipCollection();
MapRepository.Instance.Relationships[entityType] = relationships;
return new RelationshipBuilder<TEntity>(_fluentEntity, relationships);
}
}
}
}

View file

@ -0,0 +1,37 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Data.OleDb;
namespace Marr.Data.Mapping
{
public interface IColumnInfo
{
string Name { get; set; }
string AltName { get; set; }
int Size { get; set; }
bool IsPrimaryKey { get; set; }
bool IsAutoIncrement { get; set; }
bool ReturnValue { get; set; }
ParameterDirection ParamDirection { get; set; }
string TryGetAltName();
}
}

View file

@ -0,0 +1,34 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Text;
namespace Marr.Data.Mapping
{
public interface IRelationshipInfo
{
RelationshipTypes RelationType { get; set; }
Type EntityType { get; set; }
}
public enum RelationshipTypes
{
AutoDetect,
One,
Many
}
}

View file

@ -0,0 +1,208 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Marr.Data.Mapping.Strategies;
using System.Reflection;
using System.Collections;
namespace Marr.Data.Mapping
{
[Obsolete("This class is obsolete. Please use the 'Mappings' class.")]
public class MapBuilder
{
private bool _publicOnly;
public MapBuilder()
: this(true)
{ }
public MapBuilder(bool publicOnly)
{
_publicOnly = publicOnly;
}
#region - Columns -
/// <summary>
/// Creates column mappings for the given type.
/// Maps all properties except ICollection properties.
/// </summary>
/// <typeparam name="T">The type that is being built.</typeparam>
/// <returns><see cref="ColumnMapCollection"/></returns>
public ColumnMapBuilder<T> BuildColumns<T>()
{
return BuildColumns<T>(m => m.MemberType == MemberTypes.Property &&
!typeof(ICollection).IsAssignableFrom((m as PropertyInfo).PropertyType));
}
/// <summary>
/// Creates column mappings for the given type.
/// Maps all properties that are simple types (int, string, DateTime, etc).
/// ICollection properties are not included.
/// </summary>
/// <typeparam name="T">The type that is being built.</typeparam>
/// <returns><see cref="ColumnMapCollection"/></returns>
public ColumnMapBuilder<T> BuildColumnsFromSimpleTypes<T>()
{
return BuildColumns<T>(m => m.MemberType == MemberTypes.Property &&
DataHelper.IsSimpleType((m as PropertyInfo).PropertyType) &&
!typeof(ICollection).IsAssignableFrom((m as PropertyInfo).PropertyType));
}
/// <summary>
/// Creates column mappings for the given type.
/// Maps properties that are included in the include list.
/// </summary>
/// <typeparam name="T">The type that is being built.</typeparam>
/// <param name="propertiesToInclude"></param>
/// <returns><see cref="ColumnMapCollection"/></returns>
public ColumnMapBuilder<T> BuildColumns<T>(params string[] propertiesToInclude)
{
return BuildColumns<T>(m =>
m.MemberType == MemberTypes.Property &&
propertiesToInclude.Contains(m.Name));
}
/// <summary>
/// Creates column mappings for the given type.
/// Maps all properties except the ones in the exclusion list.
/// </summary>
/// <typeparam name="T">The type that is being built.</typeparam>
/// <param name="propertiesToExclude"></param>
/// <returns><see cref="ColumnMapCollection"/></returns>
public ColumnMapBuilder<T> BuildColumnsExcept<T>(params string[] propertiesToExclude)
{
return BuildColumns<T>(m =>
m.MemberType == MemberTypes.Property &&
!propertiesToExclude.Contains(m.Name));
}
/// <summary>
/// Creates column mappings for the given type if they match the predicate.
/// </summary>
/// <typeparam name="T">The type that is being built.</typeparam>
/// <param name="predicate">Determines whether a mapping should be created based on the member info.</param>
/// <returns><see cref="ColumnMapConfigurator"/></returns>
public ColumnMapBuilder<T> BuildColumns<T>(Func<MemberInfo, bool> predicate)
{
Type entityType = typeof(T);
ConventionMapStrategy strategy = new ConventionMapStrategy(_publicOnly);
strategy.ColumnPredicate = predicate;
ColumnMapCollection columns = strategy.MapColumns(entityType);
MapRepository.Instance.Columns[entityType] = columns;
return new ColumnMapBuilder<T>(null, columns);
}
/// <summary>
/// Creates a ColumnMapBuilder that starts out with no pre-populated columns.
/// All columns must be added manually using the builder.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public ColumnMapBuilder<T> Columns<T>()
{
Type entityType = typeof(T);
ColumnMapCollection columns = new ColumnMapCollection();
MapRepository.Instance.Columns[entityType] = columns;
return new ColumnMapBuilder<T>(null, columns);
}
#endregion
#region - Relationships -
/// <summary>
/// Creates relationship mappings for the given type.
/// Maps all properties that implement ICollection.
/// </summary>
/// <typeparam name="T">The type that is being built.</typeparam>
/// <returns><see cref="RelationshipBuilder"/></returns>
public RelationshipBuilder<T> BuildRelationships<T>()
{
return BuildRelationships<T>(m =>
m.MemberType == MemberTypes.Property &&
typeof(ICollection).IsAssignableFrom((m as PropertyInfo).PropertyType));
}
/// <summary>
/// Creates relationship mappings for the given type.
/// Maps all properties that are listed in the include list.
/// </summary>
/// <typeparam name="T">The type that is being built.</typeparam>
/// <param name="propertiesToInclude"></param>
/// <returns><see cref="RelationshipBuilder"/></returns>
public RelationshipBuilder<T> BuildRelationships<T>(params string[] propertiesToInclude)
{
Func<MemberInfo, bool> predicate = m =>
(
// ICollection properties
m.MemberType == MemberTypes.Property &&
typeof(ICollection).IsAssignableFrom((m as PropertyInfo).PropertyType) &&
propertiesToInclude.Contains(m.Name)
) || ( // Single entity properties
m.MemberType == MemberTypes.Property &&
!typeof(ICollection).IsAssignableFrom((m as PropertyInfo).PropertyType) &&
propertiesToInclude.Contains(m.Name)
);
return BuildRelationships<T>(predicate);
}
/// <summary>
/// Creates relationship mappings for the given type if they match the predicate.
/// </summary>
/// <typeparam name="T">The type that is being built.</typeparam>
/// <param name="predicate">Determines whether a mapping should be created based on the member info.</param>
/// <returns><see cref="RelationshipBuilder"/></returns>
public RelationshipBuilder<T> BuildRelationships<T>(Func<MemberInfo, bool> predicate)
{
Type entityType = typeof(T);
ConventionMapStrategy strategy = new ConventionMapStrategy(_publicOnly);
strategy.RelationshipPredicate = predicate;
RelationshipCollection relationships = strategy.MapRelationships(entityType);
MapRepository.Instance.Relationships[entityType] = relationships;
return new RelationshipBuilder<T>(null, relationships);
}
/// <summary>
/// Creates a RelationshipBuilder that starts out with no pre-populated relationships.
/// All relationships must be added manually using the builder.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public RelationshipBuilder<T> Relationships<T>()
{
Type entityType = typeof(T);
RelationshipCollection relationships = new RelationshipCollection();
MapRepository.Instance.Relationships[entityType] = relationships;
return new RelationshipBuilder<T>(null, relationships);
}
#endregion
#region - Tables -
/// <summary>
/// Provides a fluent table mapping interface.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public TableBuilder<T> BuildTable<T>()
{
return new TableBuilder<T>(null);
}
/// <summary>
/// Sets the table name for a given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tableName"></param>
public TableBuilder<T> BuildTable<T>(string tableName)
{
return new TableBuilder<T>(null).SetTableName(tableName);
}
#endregion
}
}

View file

@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using Marr.Data.Converters;
namespace Marr.Data.Mapping
{
internal class MappingHelper
{
private MapRepository _repos;
private IDataMapper _db;
public MappingHelper(IDataMapper db)
{
_repos = MapRepository.Instance;
_db = db;
}
/// <summary>
/// Instantiates an entity and loads its mapped fields with the data from the reader.
/// </summary>
public object CreateAndLoadEntity<T>(ColumnMapCollection mappings, DbDataReader reader, bool useAltName)
{
return CreateAndLoadEntity(typeof(T), mappings, reader, useAltName);
}
/// <summary>
/// Instantiates an entity and loads its mapped fields with the data from the reader.
/// </summary>
/// <param name="entityType">The entity being created and loaded.</param>
/// <param name="mappings">The field mappings for the passed in entity.</param>
/// <param name="reader">The open data reader.</param>
/// <param name="useAltNames">Determines if the column AltName should be used.</param>
/// <returns>Returns an entity loaded with data.</returns>
public object CreateAndLoadEntity(Type entityType, ColumnMapCollection mappings, DbDataReader reader, bool useAltName)
{
// Create new entity
object ent = _repos.ReflectionStrategy.CreateInstance(entityType);
return LoadExistingEntity(mappings, reader, ent, useAltName);
}
public object LoadExistingEntity(ColumnMapCollection mappings, DbDataReader reader, object ent, bool useAltName)
{
// Populate entity fields from data reader
foreach (ColumnMap dataMap in mappings)
{
try
{
string colName = dataMap.ColumnInfo.GetColumName(useAltName);
int ordinal = reader.GetOrdinal(colName);
object dbValue = reader.GetValue(ordinal);
// Handle conversions
IConverter conversion = _repos.GetConverter(dataMap.FieldType);
if (conversion != null)
{
dbValue = conversion.FromDB(dataMap, dbValue);
}
_repos.ReflectionStrategy.SetFieldValue(ent, dataMap.FieldName, dbValue);
}
catch (Exception ex)
{
string msg = string.Format("The DataMapper was unable to load the following field: '{0}'.",
dataMap.ColumnInfo.Name);
throw new DataMappingException(msg, ex);
}
}
PrepareLazyLoadedProperties(ent);
return ent;
}
private void PrepareLazyLoadedProperties(object ent)
{
// Handle lazy loaded properties
Type entType = ent.GetType();
if (_repos.Relationships.ContainsKey(entType))
{
Func<IDataMapper> dbCreate = () =>
{
var db = new DataMapper(_db.ProviderFactory, _db.ConnectionString);
db.SqlMode = SqlModes.Text;
return db;
};
var relationships = _repos.Relationships[entType];
foreach (var rel in relationships.Where(r => r.IsLazyLoaded))
{
ILazyLoaded lazyLoaded = (ILazyLoaded)rel.LazyLoaded.Clone();
lazyLoaded.Prepare(dbCreate, ent);
_repos.ReflectionStrategy.SetFieldValue(ent, rel.Member.Name, lazyLoaded);
}
}
}
public T LoadSimpleValueFromFirstColumn<T>(DbDataReader reader)
{
try
{
return (T)reader.GetValue(0);
}
catch (Exception ex)
{
string firstColumnName = reader.GetName(0);
string msg = string.Format("The DataMapper was unable to create a value of type '{0}' from the first column '{1}'.",
typeof(T).Name, firstColumnName);
throw new DataMappingException(msg, ex);
}
}
/// <summary>
/// Creates all parameters for a SP based on the mappings of the entity,
/// and assigns them values based on the field values of the entity.
/// </summary>
public void CreateParameters<T>(T entity, ColumnMapCollection columnMapCollection, bool isAutoQuery)
{
ColumnMapCollection mappings = columnMapCollection;
if (!isAutoQuery)
{
// Order columns (applies to Oracle and OleDb only)
mappings = columnMapCollection.OrderParameters(_db.Command);
}
foreach (ColumnMap columnMap in mappings)
{
if (columnMap.ColumnInfo.IsAutoIncrement)
continue;
var param = _db.Command.CreateParameter();
param.ParameterName = columnMap.ColumnInfo.Name;
param.Size = columnMap.ColumnInfo.Size;
param.Direction = columnMap.ColumnInfo.ParamDirection;
object val = _repos.ReflectionStrategy.GetFieldValue(entity, columnMap.FieldName);
param.Value = val == null ? DBNull.Value : val; // Convert nulls to DBNulls
var repos = MapRepository.Instance;
IConverter conversion = repos.GetConverter(columnMap.FieldType);
if (conversion != null)
{
param.Value = conversion.ToDB(param.Value);
}
// Set the appropriate DbType property depending on the parameter type
// Note: the columnMap.DBType property was set when the ColumnMap was created
repos.DbTypeBuilder.SetDbType(param, columnMap.DBType);
_db.Command.Parameters.Add(param);
}
}
/// <summary>
/// Assigns the SP result columns to the passed in 'mappings' fields.
/// </summary>
public void SetOutputValues<T>(T entity, IEnumerable<ColumnMap> mappings)
{
foreach (ColumnMap dataMap in mappings)
{
object output = _db.Command.Parameters[dataMap.ColumnInfo.Name].Value;
_repos.ReflectionStrategy.SetFieldValue(entity, dataMap.FieldName, output);
}
}
/// <summary>
/// Assigns the passed in 'value' to the passed in 'mappings' fields.
/// </summary>
public void SetOutputValues<T>(T entity, IEnumerable<ColumnMap> mappings, object value)
{
foreach (ColumnMap dataMap in mappings)
{
_repos.ReflectionStrategy.SetFieldValue(entity, dataMap.FieldName, Convert.ChangeType(value, dataMap.FieldType));
}
}
}
}

View file

@ -0,0 +1,120 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace Marr.Data.Mapping
{
public class Relationship
{
private IRelationshipInfo _relationshipInfo;
private MemberInfo _member;
private ILazyLoaded _lazyLoaded;
public Relationship(MemberInfo member)
: this(member, new RelationshipInfo())
{ }
public Relationship(MemberInfo member, IRelationshipInfo relationshipInfo)
{
_member = member;
Type memberType = ReflectionHelper.GetMemberType(member);
// Try to determine the RelationshipType
if (relationshipInfo.RelationType == RelationshipTypes.AutoDetect)
{
if (typeof(System.Collections.ICollection).IsAssignableFrom(memberType))
{
relationshipInfo.RelationType = RelationshipTypes.Many;
}
else
{
relationshipInfo.RelationType = RelationshipTypes.One;
}
}
// Try to determine the EntityType
if (relationshipInfo.EntityType == null)
{
if (relationshipInfo.RelationType == RelationshipTypes.Many)
{
if (memberType.IsGenericType)
{
// Assume a Collection<T> or List<T> and return T
relationshipInfo.EntityType = memberType.GetGenericArguments()[0];
}
else
{
throw new ArgumentException(string.Format(
"The DataMapper could not determine the RelationshipAttribute EntityType for {0}.",
memberType.Name));
}
}
else
{
relationshipInfo.EntityType = memberType;
}
}
_relationshipInfo = relationshipInfo;
}
public IRelationshipInfo RelationshipInfo
{
get { return _relationshipInfo; }
}
public MemberInfo Member
{
get { return _member; }
}
public Type MemberType
{
get
{
// Assumes that a relationship can only have a member type of Field or Property
if (Member.MemberType == MemberTypes.Field)
return (Member as FieldInfo).FieldType;
else
return (Member as PropertyInfo).PropertyType;
}
}
public bool IsLazyLoaded
{
get
{
return _lazyLoaded != null;
}
}
public ILazyLoaded LazyLoaded
{
get
{
return _lazyLoaded;
}
set
{
_lazyLoaded = value;
}
}
}
}

View file

@ -0,0 +1,77 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Text;
namespace Marr.Data.Mapping
{
/// <summary>
/// Defines a field as a related entity that needs to be created at filled with data.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class RelationshipAttribute : Attribute, IRelationshipInfo
{
/// <summary>
/// Defines a data relationship.
/// </summary>
public RelationshipAttribute()
: this(RelationshipTypes.AutoDetect)
{ }
/// <summary>
/// Defines a data relationship.
/// </summary>
/// <param name="relationType"></param>
public RelationshipAttribute(RelationshipTypes relationType)
{
RelationType = relationType;
}
/// <summary>
/// Defines a One-ToMany data relationship for a given type.
/// </summary>
/// <param name="entityType">The type of the child entity.</param>
public RelationshipAttribute(Type entityType)
: this(entityType, RelationshipTypes.AutoDetect)
{ }
/// <summary>
/// Defines a data relationship.
/// </summary>
/// <param name="entityType">The type of the child entity.</param>
/// <param name="relationType">The relationship type can be "One" or "Many".</param>
public RelationshipAttribute(Type entityType, RelationshipTypes relationType)
{
EntityType = entityType;
RelationType = relationType;
}
#region IRelationshipInfo Members
/// <summary>
/// Gets or sets the relationship type can be "One" or "Many".
/// </summary>
public RelationshipTypes RelationType { get; set; }
/// <summary>
/// Gets or sets the type of the child entity.
/// </summary>
public Type EntityType { get; set; }
#endregion
}
}

View file

@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using Marr.Data.Mapping.Strategies;
namespace Marr.Data.Mapping
{
/// <summary>
/// This class has fluent methods that are used to easily configure relationship mappings.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class RelationshipBuilder<TEntity>
{
private FluentMappings.MappingsFluentEntity<TEntity> _fluentEntity;
private string _currentPropertyName;
public RelationshipBuilder(FluentMappings.MappingsFluentEntity<TEntity> fluentEntity, RelationshipCollection relationships)
{
_fluentEntity = fluentEntity;
Relationships = relationships;
}
/// <summary>
/// Gets the list of relationship mappings that are being configured.
/// </summary>
public RelationshipCollection Relationships { get; private set; }
#region - Fluent Methods -
/// <summary>
/// Initializes the configurator to configure the given property.
/// </summary>
/// <param name="property"></param>
/// <returns></returns>
public RelationshipBuilder<TEntity> For(Expression<Func<TEntity, object>> property)
{
return For(property.GetMemberName());
}
/// <summary>
/// Initializes the configurator to configure the given property or field.
/// </summary>
/// <param name="propertyName"></param>
/// <returns></returns>
public RelationshipBuilder<TEntity> For(string propertyName)
{
_currentPropertyName = propertyName;
// Try to add the relationship if it doesn't exist
if (Relationships[_currentPropertyName] == null)
{
TryAddRelationshipForField(_currentPropertyName);
}
return this;
}
/// <summary>
/// Sets a property to be lazy loaded, with a given query.
/// </summary>
/// <typeparam name="TChild"></typeparam>
/// <param name="query"></param>
/// <returns></returns>
public RelationshipBuilder<TEntity> LazyLoad<TChild>(Func<IDataMapper, TEntity, TChild> query)
{
AssertCurrentPropertyIsSet();
Relationships[_currentPropertyName].LazyLoaded = new LazyLoaded<TEntity, TChild>(query);
return this;
}
public RelationshipBuilder<TEntity> SetOneToOne()
{
AssertCurrentPropertyIsSet();
SetOneToOne(_currentPropertyName);
return this;
}
public RelationshipBuilder<TEntity> SetOneToOne(string propertyName)
{
Relationships[propertyName].RelationshipInfo.RelationType = RelationshipTypes.One;
return this;
}
public RelationshipBuilder<TEntity> SetOneToMany()
{
AssertCurrentPropertyIsSet();
SetOneToMany(_currentPropertyName);
return this;
}
public RelationshipBuilder<TEntity> SetOneToMany(string propertyName)
{
Relationships[propertyName].RelationshipInfo.RelationType = RelationshipTypes.Many;
return this;
}
public RelationshipBuilder<TEntity> Ignore(Expression<Func<TEntity, object>> property)
{
string propertyName = property.GetMemberName();
Relationships.RemoveAll(r => r.Member.Name == propertyName);
return this;
}
public FluentMappings.MappingsFluentTables<TEntity> Tables
{
get
{
if (_fluentEntity == null)
{
throw new Exception("This property is not compatible with the obsolete 'MapBuilder' class.");
}
return _fluentEntity.Table;
}
}
public FluentMappings.MappingsFluentColumns<TEntity> Columns
{
get
{
if (_fluentEntity == null)
{
throw new Exception("This property is not compatible with the obsolete 'MapBuilder' class.");
}
return _fluentEntity.Columns;
}
}
public FluentMappings.MappingsFluentEntity<TNewEntity> Entity<TNewEntity>()
{
return new FluentMappings.MappingsFluentEntity<TNewEntity>(true);
}
/// <summary>
/// Tries to add a Relationship for the given field name.
/// Throws and exception if field cannot be found.
/// </summary>
private void TryAddRelationshipForField(string fieldName)
{
// Set strategy to filter for public or private fields
ConventionMapStrategy strategy = new ConventionMapStrategy(false);
// Find the field that matches the given field name
strategy.RelationshipPredicate = mi => mi.Name == fieldName;
Relationship relationship = strategy.MapRelationships(typeof(TEntity)).FirstOrDefault();
if (relationship == null)
{
throw new DataMappingException(string.Format("Could not find the field '{0}' in '{1}'.",
fieldName,
typeof(TEntity).Name));
}
else
{
Relationships.Add(relationship);
}
}
/// <summary>
/// Throws an exception if the "current" property has not been set.
/// </summary>
private void AssertCurrentPropertyIsSet()
{
if (string.IsNullOrEmpty(_currentPropertyName))
{
throw new DataMappingException("A property must first be specified using the 'For' method.");
}
}
#endregion
}
}

View file

@ -0,0 +1,37 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Text;
namespace Marr.Data.Mapping
{
public class RelationshipCollection : List<Relationship>
{
/// <summary>
/// Gets a ColumnMap by its field name.
/// </summary>
/// <param name="fieldName"></param>
/// <returns></returns>
public Relationship this[string fieldName]
{
get
{
return this.Find(m => m.Member.Name == fieldName);
}
}
}
}

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Marr.Data.Mapping
{
public class RelationshipInfo : IRelationshipInfo
{
public RelationshipTypes RelationType { get; set; }
public Type EntityType { get; set; }
}
}

View file

@ -0,0 +1,75 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Marr.Data.Converters;
using Marr.Data.Parameters;
namespace Marr.Data.Mapping.Strategies
{
/// <summary>
/// Maps fields or properties that are marked with the ColumnAttribute.
/// </summary>
public class AttributeMapStrategy : ReflectionMapStrategyBase
{
public AttributeMapStrategy()
: base()
{ }
public AttributeMapStrategy(bool publicOnly)
: base(publicOnly)
{ }
public AttributeMapStrategy(BindingFlags flags)
: base(flags)
{ }
/// <summary>
/// Registers any member with a ColumnAttribute as a ColumnMap.
/// <param name="entityType">The entity that is being mapped.</param>
/// <param name="member">The current member that is being inspected.</param>
/// <param name="columnAtt">A ColumnAttribute (is null of one does not exist).</param>
/// <param name="columnMaps">A list of ColumnMaps.</param>
/// </summary>
protected override void CreateColumnMap(Type entityType, MemberInfo member, ColumnAttribute columnAtt, ColumnMapCollection columnMaps)
{
if (columnAtt != null)
{
ColumnMap columnMap = new ColumnMap(member, columnAtt);
columnMaps.Add(columnMap);
}
}
/// <summary>
/// Registers any member with a RelationshipAttribute as a relationship.
/// </summary>
/// <param name="entityType">The entity that is being mapped.</param>
/// <param name="member">The current member that is being inspected.</param>
/// <param name="relationshipAtt">A RelationshipAttribute (is null if one does not exist).</param>
/// <param name="relationships">A list of Relationships.</param>
protected override void CreateRelationship(Type entityType, MemberInfo member, RelationshipAttribute relationshipAtt, RelationshipCollection relationships)
{
if (relationshipAtt != null)
{
Relationship relationship = new Relationship(member, relationshipAtt);
relationships.Add(relationship);
}
}
}
}

View file

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;
namespace Marr.Data.Mapping.Strategies
{
/// <summary>
/// Allows you to specify a member based filter by defining predicates that filter the members that are mapped.
/// </summary>
public class ConventionMapStrategy : ReflectionMapStrategyBase
{
public ConventionMapStrategy(bool publicOnly)
: base(publicOnly)
{
// Default: Only map members that are properties
ColumnPredicate = m => m.MemberType == MemberTypes.Property;
// Default: Only map members that are properties and that are ICollection types
RelationshipPredicate = m =>
{
return m.MemberType == MemberTypes.Property && typeof(ICollection).IsAssignableFrom((m as PropertyInfo).PropertyType);
};
}
public Func<MemberInfo, bool> ColumnPredicate;
public Func<MemberInfo, bool> RelationshipPredicate;
protected override void CreateColumnMap(Type entityType, System.Reflection.MemberInfo member, ColumnAttribute columnAtt, ColumnMapCollection columnMaps)
{
if (ColumnPredicate(member))
{
// Map public property to DB column
columnMaps.Add(new ColumnMap(member));
}
}
protected override void CreateRelationship(Type entityType, System.Reflection.MemberInfo member, RelationshipAttribute relationshipAtt, RelationshipCollection relationships)
{
if (RelationshipPredicate(member))
{
relationships.Add(new Relationship(member));
}
}
}
}

View file

@ -0,0 +1,48 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Marr.Data.Mapping.Strategies
{
/// <summary>
/// A strategy for creating mappings for a given entity.
/// </summary>
public interface IMapStrategy
{
/// <summary>
/// Creates a table map for a given entity type.
/// </summary>
/// <param name="entityType"></param>
/// <returns></returns>
string MapTable(Type entityType);
/// <summary>
/// Creates a ColumnMapCollection for a given entity type.
/// </summary>
/// <param name="entityType">The entity that is being mapped.</param>
ColumnMapCollection MapColumns(Type entityType);
/// <summary>
/// Creates a RelationshpCollection for a given entity type.
/// </summary>
/// <param name="entityType">The entity that is being mapped.</param>
/// <returns></returns>
RelationshipCollection MapRelationships(Type entityType);
}
}

View file

@ -0,0 +1,85 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace Marr.Data.Mapping.Strategies
{
/// <summary>
/// Maps all public properties to DB columns.
/// </summary>
public class PropertyMapStrategy : AttributeMapStrategy
{
public PropertyMapStrategy(bool publicOnly)
: base(publicOnly)
{ }
/// <summary>
/// Maps properties to DB columns if a ColumnAttribute is not present.
/// <param name="entityType">The entity that is being mapped.</param>
/// <param name="member">The current member that is being inspected.</param>
/// <param name="columnAtt">A ColumnAttribute (is null of one does not exist).</param>
/// <param name="columnMaps">A list of ColumnMaps.</param>
/// </summary>
protected override void CreateColumnMap(Type entityType, MemberInfo member, ColumnAttribute columnAtt, ColumnMapCollection columnMaps)
{
if (columnAtt != null)
{
// Add columns with ColumnAttribute
base.CreateColumnMap(entityType, member, columnAtt, columnMaps);
}
else
{
if (member.MemberType == MemberTypes.Property)
{
// Map public property to DB column
columnMaps.Add(new ColumnMap(member));
}
}
}
/// <summary>
/// Maps a relationship if a RelationshipAttribute is present.
/// </summary>
/// <param name="entityType">The entity that is being mapped.</param>
/// <param name="member">The current member that is being inspected.</param>
/// <param name="relationshipAtt">A RelationshipAttribute (is null if one does not exist).</param>
/// <param name="relationships">A list of Relationships.</param>
protected override void CreateRelationship(Type entityType, MemberInfo member, RelationshipAttribute relationshipAtt, RelationshipCollection relationships)
{
if (relationshipAtt != null)
{
// Add relationships by RelationshipAttribute
base.CreateRelationship(entityType, member, relationshipAtt, relationships);
}
else
{
if (member.MemberType == MemberTypes.Property)
{
PropertyInfo propertyInfo = member as PropertyInfo;
if (typeof(System.Collections.ICollection).IsAssignableFrom(propertyInfo.PropertyType))
{
Relationship relationship = new Relationship(member);
relationships.Add(relationship);
}
}
}
}
}
}

View file

@ -0,0 +1,151 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace Marr.Data.Mapping.Strategies
{
/// <summary>
/// Iterates through the members of an entity based on the BindingFlags, and provides an abstract method for adding ColumnMaps for each member.
/// </summary>
public abstract class ReflectionMapStrategyBase : IMapStrategy
{
private BindingFlags _bindingFlags;
/// <summary>
/// Loops through members with the following BindingFlags:
/// Instance | NonPublic | Public | FlattenHierarchy
/// </summary>
public ReflectionMapStrategyBase()
{
_bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy;
}
/// <summary>
/// Loops through members with the following BindingFlags:
/// Instance | Public | FlattenHierarchy | NonPublic (optional)
/// </summary>
/// <param name="publicOnly"></param>
public ReflectionMapStrategyBase(bool publicOnly)
{
if (publicOnly)
{
_bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy;
}
else
{
_bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy;
}
}
/// <summary>
/// Loops through members based on the passed in BindingFlags.
/// </summary>
/// <param name="bindingFlags"></param>
public ReflectionMapStrategyBase(BindingFlags bindingFlags)
{
_bindingFlags = bindingFlags;
}
public string MapTable(Type entityType)
{
object[] atts = entityType.GetCustomAttributes(typeof(TableAttribute), true);
if (atts.Length > 0)
{
return (atts[0] as TableAttribute).Name;
}
else
{
return entityType.Name;
}
}
/// <summary>
/// Implements IMapStrategy.
/// Loops through filtered members and calls the virtual "CreateColumnMap" void for each member.
/// Subclasses can override CreateColumnMap to customize adding ColumnMaps.
/// </summary>
/// <param name="entityType"></param>
/// <returns></returns>
public ColumnMapCollection MapColumns(Type entityType)
{
ColumnMapCollection columnMaps = new ColumnMapCollection();
MemberInfo[] members = entityType.GetMembers(_bindingFlags);
foreach (var member in members)
{
ColumnAttribute columnAtt = GetColumnAttribute(member);
CreateColumnMap(entityType, member, columnAtt, columnMaps);
}
return columnMaps;
}
public RelationshipCollection MapRelationships(Type entityType)
{
RelationshipCollection relationships = new RelationshipCollection();
MemberInfo[] members = entityType.GetMembers(_bindingFlags);
foreach (MemberInfo member in members)
{
RelationshipAttribute relationshipAtt = GetRelationshipAttribute(member);
CreateRelationship(entityType, member, relationshipAtt, relationships);
}
return relationships;
}
protected ColumnAttribute GetColumnAttribute(MemberInfo member)
{
if (member.IsDefined(typeof(ColumnAttribute), false))
{
return (ColumnAttribute)member.GetCustomAttributes(typeof(ColumnAttribute), false)[0];
}
return null;
}
protected RelationshipAttribute GetRelationshipAttribute(MemberInfo member)
{
if (member.IsDefined(typeof(RelationshipAttribute), false))
{
return (RelationshipAttribute)member.GetCustomAttributes(typeof(RelationshipAttribute), false)[0];
}
return null;
}
/// <summary>
/// Inspect a member and optionally add a ColumnMap.
/// </summary>
/// <param name="entityType">The entity type that is being mapped.</param>
/// <param name="member">The member that is being mapped.</param>
/// <param name="columnMaps">The ColumnMapCollection that is being created.</param>
protected abstract void CreateColumnMap(Type entityType, MemberInfo member, ColumnAttribute columnAtt, ColumnMapCollection columnMaps);
/// <summary>
/// Inspect a member and optionally add a Relationship.
/// </summary>
/// <param name="entityType">The entity that is being mapped.</param>
/// <param name="member">The current member that is being inspected.</param>
/// <param name="relationshipAtt">A RelationshipAttribute (is null if one does not exist).</param>
/// <param name="relationships">A list of Relationships.</param>
protected abstract void CreateRelationship(Type entityType, MemberInfo member, RelationshipAttribute relationshipAtt, RelationshipCollection relationships);
}
}

View file

@ -0,0 +1,43 @@
/* Copyright (C) 2008 - 2011 Jordan Marr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Marr.Data.Mapping
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class TableAttribute : Attribute
{
private string _name;
public TableAttribute()
{
}
public TableAttribute(string name)
{
_name = name;
}
public string Name
{
get { return _name; }
set { _name = value; }
}
}
}

View file

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Marr.Data.Mapping
{
/// <summary>
/// This class has fluent methods that are used to easily configure the table mapping.
/// </summary>
public class TableBuilder<TEntity>
{
private FluentMappings.MappingsFluentEntity<TEntity> _fluentEntity;
public TableBuilder(FluentMappings.MappingsFluentEntity<TEntity> fluentEntity)
{
_fluentEntity = fluentEntity;
}
#region - Fluent Methods -
public TableBuilder<TEntity> SetTableName(string tableName)
{
MapRepository.Instance.Tables[typeof(TEntity)] = tableName;
return this;
}
public FluentMappings.MappingsFluentColumns<TEntity> Columns
{
get
{
if (_fluentEntity == null)
{
throw new Exception("This property is not compatible with the obsolete 'MapBuilder' class.");
}
return _fluentEntity.Columns;
}
}
public FluentMappings.MappingsFluentRelationships<TEntity> Relationships
{
get
{
if (_fluentEntity == null)
{
throw new Exception("This property is not compatible with the obsolete 'MapBuilder' class.");
}
return _fluentEntity.Relationships;
}
}
public FluentMappings.MappingsFluentEntity<TNewEntity> Entity<TNewEntity>()
{
return new FluentMappings.MappingsFluentEntity<TNewEntity>(true);
}
#endregion
}
}