parent
58a05fcef8
commit
3cdff3bb71
Binary file not shown.
@ -0,0 +1,996 @@
|
|||||||
|
/* 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.SqlClient;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Linq;
|
||||||
|
using Marr.Data.Mapping;
|
||||||
|
using Marr.Data.Converters;
|
||||||
|
using Marr.Data.Parameters;
|
||||||
|
using Marr.Data.QGen;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Marr.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class is the main access point for making database related calls.
|
||||||
|
/// </summary>
|
||||||
|
public class DataMapper : IDataMapper
|
||||||
|
{
|
||||||
|
|
||||||
|
#region - Contructor, Members -
|
||||||
|
|
||||||
|
private DbProviderFactory _dbProviderFactory;
|
||||||
|
private string _connectionString;
|
||||||
|
private DbCommand _command;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a DataMapper for the given provider type and connection string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="providerName">Ex: </param>
|
||||||
|
/// <param name="connectionString">The database connection string.</param>
|
||||||
|
public DataMapper(string providerName, string connectionString)
|
||||||
|
: this(DbProviderFactories.GetFactory(providerName), connectionString)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A database provider agnostic initialization.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection">The database connection string.</param>
|
||||||
|
public DataMapper(DbProviderFactory dbProviderFactory, string connectionString)
|
||||||
|
{
|
||||||
|
if (dbProviderFactory == null)
|
||||||
|
throw new ArgumentNullException("dbProviderFactory instance cannot be null.");
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(connectionString))
|
||||||
|
throw new ArgumentNullException("connectionString cannot be null or empty.");
|
||||||
|
|
||||||
|
_dbProviderFactory = dbProviderFactory;
|
||||||
|
|
||||||
|
_connectionString = connectionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ConnectionString
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _connectionString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbProviderFactory ProviderFactory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _dbProviderFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new command utilizing the connection string.
|
||||||
|
/// </summary>
|
||||||
|
private DbCommand CreateNewCommand()
|
||||||
|
{
|
||||||
|
DbConnection conn = _dbProviderFactory.CreateConnection();
|
||||||
|
conn.ConnectionString = _connectionString;
|
||||||
|
DbCommand cmd = conn.CreateCommand();
|
||||||
|
SetSqlMode(cmd);
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new command utilizing the connection string with a given SQL command.
|
||||||
|
/// </summary>
|
||||||
|
private DbCommand CreateNewCommand(string sql)
|
||||||
|
{
|
||||||
|
DbCommand cmd = CreateNewCommand();
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or creates a DbCommand object.
|
||||||
|
/// </summary>
|
||||||
|
public DbCommand Command
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Lazy load
|
||||||
|
if (_command == null)
|
||||||
|
_command = CreateNewCommand();
|
||||||
|
else
|
||||||
|
SetSqlMode(_command); // Set SqlMode every time.
|
||||||
|
|
||||||
|
return _command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Parameters -
|
||||||
|
|
||||||
|
public DbParameterCollection Parameters
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Command.Parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParameterChainMethods AddParameter(string name, object value)
|
||||||
|
{
|
||||||
|
return new ParameterChainMethods(Command, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDbDataParameter AddParameter(IDbDataParameter parameter)
|
||||||
|
{
|
||||||
|
// Convert null values to DBNull.Value
|
||||||
|
if (parameter.Value == null)
|
||||||
|
parameter.Value = DBNull.Value;
|
||||||
|
|
||||||
|
this.Parameters.Add(parameter);
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - SP / SQL Mode -
|
||||||
|
|
||||||
|
private SqlModes _sqlMode = SqlModes.StoredProcedure; // Defaults to SP.
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value that determines whether the DataMapper will
|
||||||
|
/// use a stored procedure or a sql text command to access
|
||||||
|
/// the database. The default is stored procedure.
|
||||||
|
/// </summary>
|
||||||
|
public SqlModes SqlMode
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _sqlMode;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_sqlMode = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the DbCommand objects CommandType to the current SqlMode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command">The DbCommand object we are modifying.</param>
|
||||||
|
/// <returns>Returns the same DbCommand that was passed in.</returns>
|
||||||
|
private DbCommand SetSqlMode(DbCommand command)
|
||||||
|
{
|
||||||
|
if (SqlMode == SqlModes.StoredProcedure)
|
||||||
|
command.CommandType = CommandType.StoredProcedure;
|
||||||
|
else
|
||||||
|
command.CommandType = CommandType.Text;
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - ExecuteScalar, ExecuteNonQuery, ExecuteReader -
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a stored procedure that returns a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sql">The SQL command to execute.</param>
|
||||||
|
/// <returns>A scalar value</returns>
|
||||||
|
public object ExecuteScalar(string sql)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "A SQL query or stored procedure name is required");
|
||||||
|
else
|
||||||
|
Command.CommandText = sql;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
return Command.ExecuteScalar();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a non query that returns an integer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sql">The SQL command to execute.</param>
|
||||||
|
/// <returns>An integer value</returns>
|
||||||
|
public int ExecuteNonQuery(string sql)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "A SQL query or stored procedure name is required");
|
||||||
|
else
|
||||||
|
Command.CommandText = sql;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
return Command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a DataReader that can be controlled using a Func delegate.
|
||||||
|
/// (Note that reader.Read() will be called automatically).
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TResult">The type that will be return in the result set.</typeparam>
|
||||||
|
/// <param name="sql">The sql statement that will be executed.</param>
|
||||||
|
/// <param name="func">The function that will build the the TResult set.</param>
|
||||||
|
/// <returns>An IEnumerable of TResult.</returns>
|
||||||
|
public IEnumerable<TResult> ExecuteReader<TResult>(string sql, Func<DbDataReader, TResult> func)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "A SQL query or stored procedure name is required");
|
||||||
|
else
|
||||||
|
Command.CommandText = sql;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
|
||||||
|
List<TResult> list = new List<TResult>();
|
||||||
|
DbDataReader reader = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader = Command.ExecuteReader();
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
list.Add(func(reader));
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (reader != null) reader.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a DataReader that can be controlled using an Action delegate.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sql">The sql statement that will be executed.</param>
|
||||||
|
/// <param name="action">The delegate that will work with the result set.</param>
|
||||||
|
public void ExecuteReader(string sql, Action<DbDataReader> action)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "A SQL query or stored procedure name is required");
|
||||||
|
else
|
||||||
|
Command.CommandText = sql;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
|
||||||
|
DbDataReader reader = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader = Command.ExecuteReader();
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
action(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (reader != null) reader.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - DataSets -
|
||||||
|
|
||||||
|
public DataSet GetDataSet(string sql)
|
||||||
|
{
|
||||||
|
return GetDataSet(sql, new DataSet(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataSet GetDataSet(string sql, DataSet ds, string tableName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "A SQL query or stored procedure name is required");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (DbDataAdapter adapter = _dbProviderFactory.CreateDataAdapter())
|
||||||
|
{
|
||||||
|
Command.CommandText = sql;
|
||||||
|
adapter.SelectCommand = Command;
|
||||||
|
|
||||||
|
if (ds == null)
|
||||||
|
ds = new DataSet();
|
||||||
|
|
||||||
|
OpenConnection();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(tableName))
|
||||||
|
adapter.Fill(ds);
|
||||||
|
else
|
||||||
|
adapter.Fill(ds, tableName);
|
||||||
|
|
||||||
|
return ds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseConnection(); // Clears parameters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataTable GetDataTable(string sql)
|
||||||
|
{
|
||||||
|
return GetDataTable(sql, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataTable GetDataTable(string sql, DataTable dt, string tableName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "A SQL query or stored procedure name is required");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (DbDataAdapter adapter = _dbProviderFactory.CreateDataAdapter())
|
||||||
|
{
|
||||||
|
Command.CommandText = sql;
|
||||||
|
adapter.SelectCommand = Command;
|
||||||
|
|
||||||
|
if (dt == null)
|
||||||
|
dt = new DataTable();
|
||||||
|
|
||||||
|
adapter.Fill(dt);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(tableName))
|
||||||
|
dt.TableName = tableName;
|
||||||
|
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseConnection(); // Clears parameters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UpdateDataSet(DataSet ds, string sql)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "A SQL query or stored procedure name is required");
|
||||||
|
|
||||||
|
if (ds == null)
|
||||||
|
throw new ArgumentNullException("ds", "DataSet cannot be null.");
|
||||||
|
|
||||||
|
DbDataAdapter adapter = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
adapter = _dbProviderFactory.CreateDataAdapter();
|
||||||
|
|
||||||
|
adapter.UpdateCommand = Command;
|
||||||
|
adapter.UpdateCommand.CommandText = sql;
|
||||||
|
|
||||||
|
return adapter.Update(ds);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (adapter.UpdateCommand != null)
|
||||||
|
adapter.UpdateCommand.Dispose();
|
||||||
|
|
||||||
|
adapter.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int InsertDataTable(DataTable table, string insertSP)
|
||||||
|
{
|
||||||
|
return this.InsertDataTable(table, insertSP, UpdateRowSource.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int InsertDataTable(DataTable dt, string sql, UpdateRowSource updateRowSource)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "A SQL query or stored procedure name is required");
|
||||||
|
|
||||||
|
if (dt == null)
|
||||||
|
throw new ArgumentNullException("dt", "DataTable cannot be null.");
|
||||||
|
|
||||||
|
DbDataAdapter adapter = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
adapter = _dbProviderFactory.CreateDataAdapter();
|
||||||
|
|
||||||
|
adapter.InsertCommand = Command;
|
||||||
|
adapter.InsertCommand.CommandText = sql;
|
||||||
|
|
||||||
|
adapter.InsertCommand.UpdatedRowSource = updateRowSource;
|
||||||
|
|
||||||
|
return adapter.Update(dt);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (adapter.InsertCommand != null)
|
||||||
|
adapter.InsertCommand.Dispose();
|
||||||
|
|
||||||
|
adapter.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int DeleteDataTable(DataTable dt, string sql)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "A SQL query or stored procedure name is required");
|
||||||
|
|
||||||
|
if (dt == null)
|
||||||
|
throw new ArgumentNullException("dt", "DataSet cannot be null.");
|
||||||
|
|
||||||
|
DbDataAdapter adapter = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
adapter = _dbProviderFactory.CreateDataAdapter();
|
||||||
|
|
||||||
|
adapter.DeleteCommand = Command;
|
||||||
|
adapter.DeleteCommand.CommandText = sql;
|
||||||
|
|
||||||
|
return adapter.Update(dt);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (adapter.DeleteCommand != null)
|
||||||
|
adapter.DeleteCommand.Dispose();
|
||||||
|
|
||||||
|
adapter.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Find -
|
||||||
|
|
||||||
|
public T Find<T>(string sql)
|
||||||
|
{
|
||||||
|
return this.Find<T>(sql, default(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an entity of type T.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of entity that is to be instantiated and loaded with values.</typeparam>
|
||||||
|
/// <param name="sql">The SQL command to execute.</param>
|
||||||
|
/// <returns>An instantiated and loaded entity of type T.</returns>
|
||||||
|
public T Find<T>(string sql, T ent)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "A stored procedure name has not been specified for 'Find'.");
|
||||||
|
|
||||||
|
Type entityType = typeof(T);
|
||||||
|
Command.CommandText = sql;
|
||||||
|
|
||||||
|
MapRepository repository = MapRepository.Instance;
|
||||||
|
ColumnMapCollection mappings = repository.GetColumns(entityType);
|
||||||
|
|
||||||
|
bool isSimpleType = DataHelper.IsSimpleType(typeof(T));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
var mappingHelper = new MappingHelper(this);
|
||||||
|
|
||||||
|
using (DbDataReader reader = Command.ExecuteReader())
|
||||||
|
{
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
if (isSimpleType)
|
||||||
|
{
|
||||||
|
return mappingHelper.LoadSimpleValueFromFirstColumn<T>(reader);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ent == null)
|
||||||
|
ent = (T)mappingHelper.CreateAndLoadEntity<T>(mappings, reader, false);
|
||||||
|
else
|
||||||
|
mappingHelper.LoadExistingEntity(mappings, reader, ent, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Query -
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a QueryBuilder that allows you to build a query.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of object that will be queried.</typeparam>
|
||||||
|
/// <returns>Returns a QueryBuilder of T.</returns>
|
||||||
|
public QueryBuilder<T> Query<T>()
|
||||||
|
{
|
||||||
|
var dialect = QGen.QueryFactory.CreateDialect(this);
|
||||||
|
return new QueryBuilder<T>(this, dialect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the results of a query.
|
||||||
|
/// Uses a List of type T to return the data.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Returns a list of the specified type.</returns>
|
||||||
|
public List<T> Query<T>(string sql)
|
||||||
|
{
|
||||||
|
return (List<T>)Query<T>(sql, new List<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the results of a SP query.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Returns a list of the specified type.</returns>
|
||||||
|
public ICollection<T> Query<T>(string sql, ICollection<T> entityList)
|
||||||
|
{
|
||||||
|
return Query<T>(sql, entityList, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ICollection<T> Query<T>(string sql, ICollection<T> entityList, bool useAltName)
|
||||||
|
{
|
||||||
|
if (entityList == null)
|
||||||
|
throw new ArgumentNullException("entityList", "ICollection instance cannot be null.");
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "A query or stored procedure has not been specified for 'Query'.");
|
||||||
|
|
||||||
|
var mappingHelper = new MappingHelper(this);
|
||||||
|
Type entityType = typeof(T);
|
||||||
|
Command.CommandText = sql;
|
||||||
|
ColumnMapCollection mappings = MapRepository.Instance.GetColumns(entityType);
|
||||||
|
|
||||||
|
bool isSimpleType = DataHelper.IsSimpleType(typeof(T));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
using (DbDataReader reader = Command.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (isSimpleType)
|
||||||
|
{
|
||||||
|
entityList.Add(mappingHelper.LoadSimpleValueFromFirstColumn<T>(reader));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entityList.Add((T)mappingHelper.CreateAndLoadEntity<T>(mappings, reader, useAltName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
return entityList;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Query to Graph -
|
||||||
|
|
||||||
|
public List<T> QueryToGraph<T>(string sql)
|
||||||
|
{
|
||||||
|
return (List<T>)QueryToGraph<T>(sql, new List<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<T> QueryToGraph<T>(string sql, ICollection<T> entityList)
|
||||||
|
{
|
||||||
|
EntityGraph graph = new EntityGraph(typeof(T), (IList)entityList);
|
||||||
|
return QueryToGraph<T>(sql, graph, new List<MemberInfo>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries a view that joins multiple tables and returns an object graph.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="sql"></param>
|
||||||
|
/// <param name="entityList"></param>
|
||||||
|
/// <param name="entityGraph">Coordinates loading all objects in the graph..</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal ICollection<T> QueryToGraph<T>(string sql, EntityGraph graph, List<MemberInfo> childrenToLoad)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
throw new ArgumentNullException("sql", "sql");
|
||||||
|
|
||||||
|
var mappingHelper = new MappingHelper(this);
|
||||||
|
Type parentType = typeof(T);
|
||||||
|
Command.CommandText = sql;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
using (DbDataReader reader = Command.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
// The entire EntityGraph is traversed for each record,
|
||||||
|
// and multiple entities are created from each view record.
|
||||||
|
foreach (EntityGraph lvl in graph)
|
||||||
|
{
|
||||||
|
// If is child relationship entity, and childrenToLoad are specified, and entity is not listed,
|
||||||
|
// then skip this entity.
|
||||||
|
if (childrenToLoad.Count > 0 && !lvl.IsRoot && !childrenToLoad.ContainsMember(lvl.Member)) // lvl.Member.Name
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lvl.IsNewGroup(reader))
|
||||||
|
{
|
||||||
|
var newEntity = mappingHelper.CreateAndLoadEntity(lvl.EntityType, lvl.Columns, reader, true);
|
||||||
|
|
||||||
|
// Add entity to the appropriate place in the object graph
|
||||||
|
lvl.AddEntity(newEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ICollection<T>)graph.RootList;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Update -
|
||||||
|
|
||||||
|
public UpdateQueryBuilder<T> Update<T>()
|
||||||
|
{
|
||||||
|
return new UpdateQueryBuilder<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Update<T>(T entity, Expression<Func<T, bool>> filter)
|
||||||
|
{
|
||||||
|
return Update<T>()
|
||||||
|
.Entity(entity)
|
||||||
|
.Where(filter)
|
||||||
|
.Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Update<T>(string tableName, T entity, Expression<Func<T, bool>> filter)
|
||||||
|
{
|
||||||
|
return Update<T>()
|
||||||
|
.TableName(tableName)
|
||||||
|
.Entity(entity)
|
||||||
|
.Where(filter)
|
||||||
|
.Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Update<T>(T entity, string sql)
|
||||||
|
{
|
||||||
|
return Update<T>()
|
||||||
|
.Entity(entity)
|
||||||
|
.QueryText(sql)
|
||||||
|
.Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Insert -
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an InsertQueryBuilder that allows you to build an insert statement.
|
||||||
|
/// This method gives you the flexibility to manually configure all options of your insert statement.
|
||||||
|
/// Note: You must manually call the Execute() chaining method to run the query.
|
||||||
|
/// </summary>
|
||||||
|
public InsertQueryBuilder<T> Insert<T>()
|
||||||
|
{
|
||||||
|
return new InsertQueryBuilder<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates and executes an insert query for the given entity.
|
||||||
|
/// This overload will automatically run an identity query if you have mapped an auto-incrementing column,
|
||||||
|
/// and if an identity query has been implemented for your current database dialect.
|
||||||
|
/// </summary>
|
||||||
|
public object Insert<T>(T entity)
|
||||||
|
{
|
||||||
|
var columns = MapRepository.Instance.GetColumns(typeof(T));
|
||||||
|
var dialect = QueryFactory.CreateDialect(this);
|
||||||
|
var builder = Insert<T>().Entity(entity);
|
||||||
|
|
||||||
|
// If an auto-increment column exists and this dialect has an identity query...
|
||||||
|
if (columns.Exists(c => c.ColumnInfo.IsAutoIncrement) && dialect.HasIdentityQuery)
|
||||||
|
{
|
||||||
|
builder.GetIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates and executes an insert query for the given entity.
|
||||||
|
/// This overload will automatically run an identity query if you have mapped an auto-incrementing column,
|
||||||
|
/// and if an identity query has been implemented for your current database dialect.
|
||||||
|
/// </summary>
|
||||||
|
public object Insert<T>(string tableName, T entity)
|
||||||
|
{
|
||||||
|
var columns = MapRepository.Instance.GetColumns(typeof(T));
|
||||||
|
var dialect = QueryFactory.CreateDialect(this);
|
||||||
|
var builder = Insert<T>().Entity(entity).TableName(tableName);
|
||||||
|
|
||||||
|
// If an auto-increment column exists and this dialect has an identity query...
|
||||||
|
if (columns.Exists(c => c.ColumnInfo.IsAutoIncrement) && dialect.HasIdentityQuery)
|
||||||
|
{
|
||||||
|
builder.GetIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes an insert query for the given entity using the given sql insert statement.
|
||||||
|
/// This overload will automatically run an identity query if you have mapped an auto-incrementing column,
|
||||||
|
/// and if an identity query has been implemented for your current database dialect.
|
||||||
|
/// </summary>
|
||||||
|
public object Insert<T>(T entity, string sql)
|
||||||
|
{
|
||||||
|
var columns = MapRepository.Instance.GetColumns(typeof(T));
|
||||||
|
var dialect = QueryFactory.CreateDialect(this);
|
||||||
|
var builder = Insert<T>().Entity(entity).QueryText(sql);
|
||||||
|
|
||||||
|
// If an auto-increment column exists and this dialect has an identity query...
|
||||||
|
if (columns.Exists(c => c.ColumnInfo.IsAutoIncrement) && dialect.HasIdentityQuery)
|
||||||
|
{
|
||||||
|
builder.GetIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Delete -
|
||||||
|
|
||||||
|
public int Delete<T>(Expression<Func<T, bool>> filter)
|
||||||
|
{
|
||||||
|
return Delete<T>(null, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Delete<T>(string tableName, Expression<Func<T, bool>> filter)
|
||||||
|
{
|
||||||
|
// Remember sql mode
|
||||||
|
var previousSqlMode = this.SqlMode;
|
||||||
|
SqlMode = SqlModes.Text;
|
||||||
|
|
||||||
|
var mappingHelper = new MappingHelper(this);
|
||||||
|
if (tableName == null)
|
||||||
|
{
|
||||||
|
tableName = MapRepository.Instance.GetTableName(typeof(T));
|
||||||
|
}
|
||||||
|
var dialect = QGen.QueryFactory.CreateDialect(this);
|
||||||
|
TableCollection tables = new TableCollection();
|
||||||
|
tables.Add(new Table(typeof(T)));
|
||||||
|
var where = new WhereBuilder<T>(Command, dialect, filter, tables, false, false);
|
||||||
|
IQuery query = QueryFactory.CreateDeleteQuery(dialect, tables[0], where.ToString());
|
||||||
|
Command.CommandText = query.Generate();
|
||||||
|
|
||||||
|
int rowsAffected = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
rowsAffected = Command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return to previous sql mode
|
||||||
|
SqlMode = previousSqlMode;
|
||||||
|
|
||||||
|
return rowsAffected;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Events -
|
||||||
|
|
||||||
|
public event EventHandler OpeningConnection;
|
||||||
|
|
||||||
|
public event EventHandler ClosingConnection;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Connections / Transactions -
|
||||||
|
|
||||||
|
protected virtual void OnOpeningConnection()
|
||||||
|
{
|
||||||
|
if (OpeningConnection != null)
|
||||||
|
OpeningConnection(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnClosingConnection()
|
||||||
|
{
|
||||||
|
WriteToTraceLog();
|
||||||
|
|
||||||
|
if (ClosingConnection != null)
|
||||||
|
ClosingConnection(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal void OpenConnection()
|
||||||
|
{
|
||||||
|
OnOpeningConnection();
|
||||||
|
|
||||||
|
if (Command.Connection.State != ConnectionState.Open)
|
||||||
|
Command.Connection.Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal void CloseConnection()
|
||||||
|
{
|
||||||
|
OnClosingConnection();
|
||||||
|
|
||||||
|
Command.Parameters.Clear();
|
||||||
|
Command.CommandText = string.Empty;
|
||||||
|
|
||||||
|
if (Command.Transaction == null)
|
||||||
|
Command.Connection.Close(); // Only close if no transaction is present
|
||||||
|
|
||||||
|
UnbindEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteToTraceLog()
|
||||||
|
{
|
||||||
|
if (MapRepository.Instance.EnableTraceLogging)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("==== Begin Query Trace ====");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("QUERY TYPE:");
|
||||||
|
sb.AppendLine(Command.CommandType.ToString());
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("QUERY TEXT:");
|
||||||
|
sb.AppendLine(Command.CommandText);
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("PARAMETERS:");
|
||||||
|
foreach (IDbDataParameter p in Parameters)
|
||||||
|
{
|
||||||
|
object val = (p.Value != null && p.Value is string) ? string.Format("\"{0}\"", p.Value) : p.Value;
|
||||||
|
sb.AppendFormat("{0} = [{1}]", p.ParameterName, val ?? "NULL").AppendLine();
|
||||||
|
}
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("==== End Query Trace ====");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
Trace.Write(sb.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnbindEvents()
|
||||||
|
{
|
||||||
|
if (OpeningConnection != null)
|
||||||
|
OpeningConnection = null;
|
||||||
|
|
||||||
|
if (ClosingConnection != null)
|
||||||
|
ClosingConnection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginTransaction()
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
DbTransaction trans = Command.Connection.BeginTransaction();
|
||||||
|
Command.Transaction = trans;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RollBack()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Command.Transaction != null)
|
||||||
|
Command.Transaction.Rollback();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Command.Connection.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Commit()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Command.Transaction != null)
|
||||||
|
Command.Transaction.Commit();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Command.Connection.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - IDisposable Members -
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this); // In case a derived class implements a finalizer
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
if (Command.Transaction != null)
|
||||||
|
{
|
||||||
|
Command.Transaction.Dispose();
|
||||||
|
Command.Transaction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Command.Connection != null)
|
||||||
|
{
|
||||||
|
Command.Connection.Dispose();
|
||||||
|
Command.Connection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Command != null)
|
||||||
|
{
|
||||||
|
Command.Dispose();
|
||||||
|
_command = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,376 @@
|
|||||||
|
/* 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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Linq;
|
||||||
|
using Marr.Data.Mapping;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Marr.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Holds metadata about an object graph that is being queried and eagerly loaded.
|
||||||
|
/// Contains all metadata needed to instantiate the object and fill it with data from a DataReader.
|
||||||
|
/// Does not iterate through lazy loaded child relationships.
|
||||||
|
/// </summary>
|
||||||
|
internal class EntityGraph : IEnumerable<EntityGraph>
|
||||||
|
{
|
||||||
|
private MapRepository _repos;
|
||||||
|
private EntityGraph _parent;
|
||||||
|
private Type _entityType;
|
||||||
|
private Relationship _relationship;
|
||||||
|
private ColumnMapCollection _columns;
|
||||||
|
private RelationshipCollection _relationships;
|
||||||
|
private List<EntityGraph> _children;
|
||||||
|
private object _entity;
|
||||||
|
private GroupingKeyCollection _groupingKeyColumns;
|
||||||
|
private Dictionary<string, EntityReference> _entityReferences;
|
||||||
|
|
||||||
|
public IList RootList { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recursively builds an entity graph of the given parent type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entityType"></param>
|
||||||
|
public EntityGraph(Type entityType, IList rootList)
|
||||||
|
: this(entityType, null, null) // Recursively constructs hierarchy
|
||||||
|
{
|
||||||
|
RootList = rootList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recursively builds entity graph hierarchy.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entityType"></param>
|
||||||
|
/// <param name="parent"></param>
|
||||||
|
/// <param name="relationship"></param>
|
||||||
|
private EntityGraph(Type entityType, EntityGraph parent, Relationship relationship)
|
||||||
|
{
|
||||||
|
_repos = MapRepository.Instance;
|
||||||
|
|
||||||
|
_entityType = entityType;
|
||||||
|
_parent = parent;
|
||||||
|
_relationship = relationship;
|
||||||
|
_columns = _repos.GetColumns(entityType);
|
||||||
|
_relationships = _repos.GetRelationships(entityType);
|
||||||
|
_children = new List<EntityGraph>();
|
||||||
|
Member = relationship != null ? relationship.Member : null;
|
||||||
|
_entityReferences = new Dictionary<string, EntityReference>();
|
||||||
|
|
||||||
|
// Create a new EntityGraph for each child relationship that is not lazy loaded
|
||||||
|
foreach (Relationship childRelationship in this.Relationships)
|
||||||
|
{
|
||||||
|
if (!childRelationship.IsLazyLoaded)
|
||||||
|
{
|
||||||
|
_children.Add(new EntityGraph(childRelationship.RelationshipInfo.EntityType, this, childRelationship));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemberInfo Member { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parent of this EntityGraph.
|
||||||
|
/// </summary>
|
||||||
|
public EntityGraph Parent
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Type of this EntityGraph.
|
||||||
|
/// </summary>
|
||||||
|
public Type EntityType
|
||||||
|
{
|
||||||
|
get { return _entityType; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a boolean than indicates whether this entity is the root node in the graph.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRoot
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _parent == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a boolean that indicates whether this entity is a child.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsChild
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _parent != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the columns mapped to this entity.
|
||||||
|
/// </summary>
|
||||||
|
public ColumnMapCollection Columns
|
||||||
|
{
|
||||||
|
get { return _columns; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the relationships mapped to this entity.
|
||||||
|
/// </summary>
|
||||||
|
public RelationshipCollection Relationships
|
||||||
|
{
|
||||||
|
get { return _relationships; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of EntityGraph objects that hold metadata about the child entities that will be loaded.
|
||||||
|
/// </summary>
|
||||||
|
public List<EntityGraph> Children
|
||||||
|
{
|
||||||
|
get { return _children; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds an entity to the appropriate place in the object graph.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entityInstance"></param>
|
||||||
|
public void AddEntity(object entityInstance)
|
||||||
|
{
|
||||||
|
_entity = entityInstance;
|
||||||
|
|
||||||
|
// Add newly created entityInstance to list (Many) or set it to field (One)
|
||||||
|
if (this.IsRoot)
|
||||||
|
{
|
||||||
|
RootList.Add(entityInstance);
|
||||||
|
}
|
||||||
|
else if (_relationship.RelationshipInfo.RelationType == RelationshipTypes.Many)
|
||||||
|
{
|
||||||
|
var list = _parent._entityReferences[_parent.GroupingKeyColumns.GroupingKey]
|
||||||
|
.ChildLists[_relationship.Member.Name];
|
||||||
|
|
||||||
|
list.Add(entityInstance);
|
||||||
|
}
|
||||||
|
else // RelationTypes.One
|
||||||
|
{
|
||||||
|
_repos.ReflectionStrategy.SetFieldValue(_parent._entity, _relationship.Member.Name, entityInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityReference entityRef = new EntityReference(entityInstance);
|
||||||
|
_entityReferences.Add(GroupingKeyColumns.GroupingKey, entityRef);
|
||||||
|
|
||||||
|
InitOneToManyChildLists(entityRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the owning lists on many-to-many Children.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entityInstance"></param>
|
||||||
|
private void InitOneToManyChildLists(EntityReference entityRef)
|
||||||
|
{
|
||||||
|
// Get a reference to the parent's the childrens' OwningLists to the parent entity
|
||||||
|
for (int i = 0; i < Relationships.Count; i++)
|
||||||
|
{
|
||||||
|
Relationship relationship = Relationships[i];
|
||||||
|
if (relationship.RelationshipInfo.RelationType == RelationshipTypes.Many)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IList list = (IList)_repos.ReflectionStrategy.CreateInstance(relationship.MemberType);
|
||||||
|
_repos.ReflectionStrategy.SetFieldValue(entityRef.Entity, relationship.Member.Name, list);
|
||||||
|
|
||||||
|
// Save a reference to each 1-M list
|
||||||
|
entityRef.AddChildList(relationship.Member.Name, list);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new DataMappingException(
|
||||||
|
string.Format("{0}.{1} is a \"Many\" relationship type so it must derive from IList.",
|
||||||
|
entityRef.Entity.GetType().Name, relationship.Member.Name),
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recursively adds primary key columns from contiguous child graphs with a one-to-one relationship type to the pKeys collection..
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pKeys"></param>
|
||||||
|
/// <param name="entity"></param>
|
||||||
|
private void AddOneToOneChildKeys(ColumnMapCollection pKeys, EntityGraph entity)
|
||||||
|
{
|
||||||
|
var oneToOneChildren = entity.Children
|
||||||
|
.Where(c => c._relationship.RelationshipInfo.RelationType == RelationshipTypes.One);
|
||||||
|
|
||||||
|
foreach (var child in oneToOneChildren)
|
||||||
|
{
|
||||||
|
pKeys.AddRange(child.Columns.PrimaryKeys);
|
||||||
|
AddOneToOneChildKeys(pKeys, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Concatenates the values of the GroupingKeys property and compares them
|
||||||
|
/// against the LastKeyGroup property. Returns true if the values are different,
|
||||||
|
/// or false if the values are the same.
|
||||||
|
/// The currently concatenated keys are saved in the LastKeyGroup property.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool IsNewGroup(DbDataReader reader)
|
||||||
|
{
|
||||||
|
bool isNewGroup = false;
|
||||||
|
|
||||||
|
// Get primary keys from parent entity and any one-to-one child entites
|
||||||
|
GroupingKeyCollection groupingKeyColumns = this.GroupingKeyColumns;
|
||||||
|
|
||||||
|
// Concatenate column values
|
||||||
|
KeyGroupInfo keyGroupInfo = groupingKeyColumns.CreateGroupingKey(reader);
|
||||||
|
|
||||||
|
if (!keyGroupInfo.HasNullKey && !_entityReferences.ContainsKey(keyGroupInfo.GroupingKey))
|
||||||
|
{
|
||||||
|
isNewGroup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isNewGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the GroupingKeys for this entity.
|
||||||
|
/// GroupingKeys determine when to create and add a new entity to the graph.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// A simple entity with no relationships will return only its PrimaryKey columns.
|
||||||
|
/// A parent entity with one-to-one child relationships will include its own PrimaryKeys,
|
||||||
|
/// and it will recursively traverse all Children with one-to-one relationships and add their PrimaryKeys.
|
||||||
|
/// A child entity that has a one-to-one relationship with its parent will use the same
|
||||||
|
/// GroupingKeys already defined by its parent.
|
||||||
|
/// </remarks>
|
||||||
|
public GroupingKeyCollection GroupingKeyColumns
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_groupingKeyColumns == null)
|
||||||
|
_groupingKeyColumns = GetGroupingKeyColumns();
|
||||||
|
|
||||||
|
return _groupingKeyColumns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of keys to group by.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When converting an unnormalized set of data from a database view,
|
||||||
|
/// a new entity is only created when the grouping keys have changed.
|
||||||
|
/// NOTE: This behavior works on the assumption that the view result set
|
||||||
|
/// has been sorted by the root entity primary key(s), followed by the
|
||||||
|
/// child entity primary keys.
|
||||||
|
/// </remarks>
|
||||||
|
/// <returns></returns>
|
||||||
|
private GroupingKeyCollection GetGroupingKeyColumns()
|
||||||
|
{
|
||||||
|
// Get primary keys for this parent entity
|
||||||
|
GroupingKeyCollection groupingKeyColumns = new GroupingKeyCollection();
|
||||||
|
groupingKeyColumns.PrimaryKeys.AddRange(Columns.PrimaryKeys);
|
||||||
|
|
||||||
|
// The following conditions should fail with an exception:
|
||||||
|
// 1) Any parent entity (entity with children) must have at least one PK specified or an exception will be thrown
|
||||||
|
// 2) All 1-M relationship entities must have at least one PK specified
|
||||||
|
// * Only 1-1 entities with no children are allowed to have 0 PKs specified.
|
||||||
|
if ((groupingKeyColumns.PrimaryKeys.Count == 0 && _children.Count > 0) ||
|
||||||
|
(groupingKeyColumns.PrimaryKeys.Count == 0 && !IsRoot && _relationship.RelationshipInfo.RelationType == RelationshipTypes.Many))
|
||||||
|
throw new MissingPrimaryKeyException(string.Format("There are no primary key mappings defined for the following entity: '{0}'.", this.EntityType.Name));
|
||||||
|
|
||||||
|
// Add parent's keys
|
||||||
|
if (IsChild)
|
||||||
|
groupingKeyColumns.ParentPrimaryKeys.AddRange(Parent.GroupingKeyColumns);
|
||||||
|
|
||||||
|
return groupingKeyColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IEnumerable<EntityGraph> Members
|
||||||
|
|
||||||
|
public IEnumerator<EntityGraph> GetEnumerator()
|
||||||
|
{
|
||||||
|
return TraverseGraph(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recursively traverses through every entity in the EntityGraph.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entityGraph"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static IEnumerator<EntityGraph> TraverseGraph(EntityGraph entityGraph)
|
||||||
|
{
|
||||||
|
Stack<EntityGraph> stack = new Stack<EntityGraph>();
|
||||||
|
stack.Push(entityGraph);
|
||||||
|
|
||||||
|
while (stack.Count > 0)
|
||||||
|
{
|
||||||
|
EntityGraph node = stack.Pop();
|
||||||
|
yield return node;
|
||||||
|
|
||||||
|
foreach (EntityGraph childGraph in node.Children)
|
||||||
|
{
|
||||||
|
stack.Push(childGraph);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IEnumerable Members
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return this.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct KeyGroupInfo
|
||||||
|
{
|
||||||
|
private string _groupingKey;
|
||||||
|
private bool _hasNullKey;
|
||||||
|
|
||||||
|
public KeyGroupInfo(string groupingKey, bool hasNullKey)
|
||||||
|
{
|
||||||
|
_groupingKey = groupingKey;
|
||||||
|
_hasNullKey = hasNullKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GroupingKey
|
||||||
|
{
|
||||||
|
get { return _groupingKey; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasNullKey
|
||||||
|
{
|
||||||
|
get { return _hasNullKey; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,265 @@
|
|||||||
|
/* 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;
|
||||||
|
using System.Data.Common;
|
||||||
|
using Marr.Data.Converters;
|
||||||
|
using Marr.Data.Parameters;
|
||||||
|
using Marr.Data.Mapping;
|
||||||
|
using Marr.Data.Mapping.Strategies;
|
||||||
|
using Marr.Data.Reflection;
|
||||||
|
|
||||||
|
namespace Marr.Data
|
||||||
|
{
|
||||||
|
public class MapRepository
|
||||||
|
{
|
||||||
|
private static readonly object _tablesLock = new object();
|
||||||
|
private static readonly object _columnsLock = new object();
|
||||||
|
private static readonly object _relationshipsLock = new object();
|
||||||
|
|
||||||
|
private IDbTypeBuilder _dbTypeBuilder;
|
||||||
|
private Dictionary<Type, IMapStrategy> _columnMapStrategies;
|
||||||
|
|
||||||
|
internal Dictionary<Type, string> Tables { get; set; }
|
||||||
|
internal Dictionary<Type, ColumnMapCollection> Columns { get; set; }
|
||||||
|
internal Dictionary<Type, RelationshipCollection> Relationships { get; set; }
|
||||||
|
internal Dictionary<Type, IConverter> TypeConverters { get; set; }
|
||||||
|
|
||||||
|
// Explicit static constructor to tell C# compiler
|
||||||
|
// not to mark type as beforefieldinit
|
||||||
|
static MapRepository()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private MapRepository()
|
||||||
|
{
|
||||||
|
Tables = new Dictionary<Type, string>();
|
||||||
|
Columns = new Dictionary<Type, ColumnMapCollection>();
|
||||||
|
Relationships = new Dictionary<Type, RelationshipCollection>();
|
||||||
|
TypeConverters = new Dictionary<Type, IConverter>();
|
||||||
|
|
||||||
|
// Register a default IReflectionStrategy
|
||||||
|
ReflectionStrategy = new CachedReflectionStrategy();
|
||||||
|
|
||||||
|
// Register a default type converter for Enums
|
||||||
|
TypeConverters.Add(typeof(Enum), new Converters.EnumStringConverter());
|
||||||
|
|
||||||
|
// Register a default IDbTypeBuilder
|
||||||
|
_dbTypeBuilder = new Parameters.DbTypeBuilder();
|
||||||
|
|
||||||
|
_columnMapStrategies = new Dictionary<Type, IMapStrategy>();
|
||||||
|
RegisterDefaultMapStrategy(new AttributeMapStrategy());
|
||||||
|
|
||||||
|
EnableTraceLogging = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly static MapRepository _instance = new MapRepository();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a reference to the singleton MapRepository.
|
||||||
|
/// </summary>
|
||||||
|
public static MapRepository Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a boolean that determines whether debug information should be written to the trace log.
|
||||||
|
/// The default is false.
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableTraceLogging { get; set; }
|
||||||
|
|
||||||
|
#region - Column Map Strategies -
|
||||||
|
|
||||||
|
public void RegisterDefaultMapStrategy(IMapStrategy strategy)
|
||||||
|
{
|
||||||
|
RegisterMapStrategy(typeof(object), strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterMapStrategy(Type entityType, IMapStrategy strategy)
|
||||||
|
{
|
||||||
|
if (_columnMapStrategies.ContainsKey(entityType))
|
||||||
|
_columnMapStrategies[entityType] = strategy;
|
||||||
|
else
|
||||||
|
_columnMapStrategies.Add(entityType, strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IMapStrategy GetMapStrategy(Type entityType)
|
||||||
|
{
|
||||||
|
if (_columnMapStrategies.ContainsKey(entityType))
|
||||||
|
{
|
||||||
|
// Return entity specific column map strategy
|
||||||
|
return _columnMapStrategies[entityType];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Return the default column map strategy
|
||||||
|
return _columnMapStrategies[typeof(object)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Table repository -
|
||||||
|
|
||||||
|
internal string GetTableName(Type entityType)
|
||||||
|
{
|
||||||
|
if (!Tables.ContainsKey(entityType))
|
||||||
|
{
|
||||||
|
lock (_tablesLock)
|
||||||
|
{
|
||||||
|
if (!Tables.ContainsKey(entityType))
|
||||||
|
{
|
||||||
|
string tableName = GetMapStrategy(entityType).MapTable(entityType);
|
||||||
|
Tables.Add(entityType, tableName);
|
||||||
|
return tableName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Tables[entityType];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Columns repository -
|
||||||
|
|
||||||
|
public ColumnMapCollection GetColumns(Type entityType)
|
||||||
|
{
|
||||||
|
if (!Columns.ContainsKey(entityType))
|
||||||
|
{
|
||||||
|
lock (_columnsLock)
|
||||||
|
{
|
||||||
|
if (!Columns.ContainsKey(entityType))
|
||||||
|
{
|
||||||
|
ColumnMapCollection columnMaps = GetMapStrategy(entityType).MapColumns(entityType);
|
||||||
|
Columns.Add(entityType, columnMaps);
|
||||||
|
return columnMaps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Columns[entityType];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Relationships repository -
|
||||||
|
|
||||||
|
public RelationshipCollection GetRelationships(Type type)
|
||||||
|
{
|
||||||
|
if (!Relationships.ContainsKey(type))
|
||||||
|
{
|
||||||
|
lock (_relationshipsLock)
|
||||||
|
{
|
||||||
|
if (!Relationships.ContainsKey(type))
|
||||||
|
{
|
||||||
|
RelationshipCollection relationships = GetMapStrategy(type).MapRelationships(type);
|
||||||
|
Relationships.Add(type, relationships);
|
||||||
|
return relationships;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Relationships[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Reflection Strategy -
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the reflection strategy that the DataMapper will use to load entities.
|
||||||
|
/// By default the CachedReflector will be used, which provides a performance increase over the SimpleReflector.
|
||||||
|
/// However, the SimpleReflector can be used in Medium Trust enviroments.
|
||||||
|
/// </summary>
|
||||||
|
public IReflectionStrategy ReflectionStrategy { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - Type Converters -
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a converter for a given type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The CLR data type that will be converted.</param>
|
||||||
|
/// <param name="converter">An IConverter object that will handle the data conversion.</param>
|
||||||
|
public void RegisterTypeConverter(Type type, IConverter converter)
|
||||||
|
{
|
||||||
|
if (TypeConverters.ContainsKey(type))
|
||||||
|
{
|
||||||
|
TypeConverters[type] = converter;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TypeConverters.Add(type, converter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks for a type converter (if one exists).
|
||||||
|
/// 1) Checks for a converter registered for the current columns data type.
|
||||||
|
/// 2) Checks to see if a converter is registered for all enums (type of Enum) if the current column is an enum.
|
||||||
|
/// 3) Checks to see if a converter is registered for all objects (type of Object).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataMap">The current data map.</param>
|
||||||
|
/// <returns>Returns an IConverter object or null if one does not exist.</returns>
|
||||||
|
internal IConverter GetConverter(Type dataType)
|
||||||
|
{
|
||||||
|
if (TypeConverters.ContainsKey(dataType))
|
||||||
|
{
|
||||||
|
// User registered type converter
|
||||||
|
return TypeConverters[dataType];
|
||||||
|
}
|
||||||
|
else if (TypeConverters.ContainsKey(typeof(Enum)) && dataType.IsEnum)
|
||||||
|
{
|
||||||
|
// A converter is registered to handled enums
|
||||||
|
return TypeConverters[typeof(Enum)];
|
||||||
|
}
|
||||||
|
else if (TypeConverters.ContainsKey(typeof(object)))
|
||||||
|
{
|
||||||
|
// User registered default converter
|
||||||
|
return TypeConverters[typeof(object)];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No conversion
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region - DbTypeBuilder -
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the IDBTypeBuilder that is responsible for converting parameter DbTypes based on the parameter value.
|
||||||
|
/// Defaults to use the DbTypeBuilder.
|
||||||
|
/// You can replace this with a more specific builder if you want more control over the way the parameter types are set.
|
||||||
|
/// </summary>
|
||||||
|
public IDbTypeBuilder DbTypeBuilder
|
||||||
|
{
|
||||||
|
get { return _dbTypeBuilder; }
|
||||||
|
set { _dbTypeBuilder = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<package xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<metadata xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
|
<id>MarrDataMapper</id>
|
||||||
|
<version>
|
||||||
|
3.17.4747.34302
|
||||||
|
</version>
|
||||||
|
<authors>Jordan Marr</authors>
|
||||||
|
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||||
|
<description>Marr Data Mapper is a Linq enabled ORM that allows you to project views into complex object graphs. Contributors: Rick Schott, vitidev.</description>
|
||||||
|
<language>en-US</language>
|
||||||
|
<projectUrl>http://marrdatamapper.codeplex.com/</projectUrl>
|
||||||
|
<licenseUrl>http://marrdatamapper.codeplex.com/license</licenseUrl>
|
||||||
|
<tags>ORM data mapper fluent linq sql relational database DAL entity</tags>
|
||||||
|
</metadata>
|
||||||
|
<files>
|
||||||
|
<file src="bin\Release\*.dll" target="lib" />
|
||||||
|
</files>
|
||||||
|
</package>
|
||||||
|
|
@ -0,0 +1,27 @@
|
|||||||
|
/* 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
|
||||||
|
{
|
||||||
|
public enum SqlModes
|
||||||
|
{
|
||||||
|
StoredProcedure,
|
||||||
|
Text
|
||||||
|
}
|
||||||
|
}
|
@ -1,118 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Core.Datastore;
|
|
||||||
using NzbDrone.Core.MediaFiles;
|
|
||||||
using NzbDrone.Core.Model.Notification;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Jobs.Implementations
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This job processes newly added jobs by downloading their info
|
|
||||||
/// from TheTVDB.org and doing a disk scan. this job should only
|
|
||||||
/// </summary>
|
|
||||||
public class ImportNewSeriesJob : IJob
|
|
||||||
{
|
|
||||||
private readonly IMediaFileService _mediaFileService;
|
|
||||||
private readonly UpdateInfoJob _updateInfoJob;
|
|
||||||
private readonly DiskScanJob _diskScanJob;
|
|
||||||
private readonly ISeasonRepository _seasonRepository;
|
|
||||||
private readonly XemUpdateJob _xemUpdateJob;
|
|
||||||
private readonly ISeriesRepository _seriesRepository;
|
|
||||||
private readonly ISeasonService _seasonService;
|
|
||||||
|
|
||||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
|
||||||
|
|
||||||
private List<int> _attemptedSeries;
|
|
||||||
|
|
||||||
public ImportNewSeriesJob(IMediaFileService mediaFileService, UpdateInfoJob updateInfoJob,
|
|
||||||
DiskScanJob diskScanJob,
|
|
||||||
ISeasonRepository seasonRepository, XemUpdateJob xemUpdateJob, ISeriesRepository seriesRepository, ISeasonService seasonService)
|
|
||||||
{
|
|
||||||
_mediaFileService = mediaFileService;
|
|
||||||
_updateInfoJob = updateInfoJob;
|
|
||||||
_diskScanJob = diskScanJob;
|
|
||||||
_seasonRepository = seasonRepository;
|
|
||||||
_xemUpdateJob = xemUpdateJob;
|
|
||||||
_seriesRepository = seriesRepository;
|
|
||||||
_seasonService = seasonService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get { return "Import New Series"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public TimeSpan DefaultInterval
|
|
||||||
{
|
|
||||||
get { return TimeSpan.FromHours(6); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start(ProgressNotification notification, dynamic options)
|
|
||||||
{
|
|
||||||
_attemptedSeries = new List<int>();
|
|
||||||
ScanSeries(notification);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ScanSeries(ProgressNotification notification)
|
|
||||||
{
|
|
||||||
var syncList = _seriesRepository.All().Where(s => s.LastInfoSync == null && !_attemptedSeries.Contains(s.Id)).ToList();
|
|
||||||
if (syncList.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var currentSeries in syncList)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_attemptedSeries.Add(((ModelBase)currentSeries).Id);
|
|
||||||
notification.CurrentMessage = String.Format("Searching for '{0}'", new DirectoryInfo(currentSeries.Path).Name);
|
|
||||||
|
|
||||||
_updateInfoJob.Start(notification, new { SeriesId = ((ModelBase)currentSeries).Id });
|
|
||||||
_diskScanJob.Start(notification, new { SeriesId = ((ModelBase)currentSeries).Id });
|
|
||||||
|
|
||||||
var updatedSeries = _seriesRepository.Get(((ModelBase)currentSeries).Id);
|
|
||||||
AutoIgnoreSeasons(((ModelBase)updatedSeries).Id);
|
|
||||||
|
|
||||||
//Get Scene Numbering if applicable
|
|
||||||
_xemUpdateJob.Start(notification, new { SeriesId = ((ModelBase)updatedSeries).Id });
|
|
||||||
|
|
||||||
notification.CurrentMessage = String.Format("{0} was successfully imported", updatedSeries.Title);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.ErrorException(e.Message, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Keep scanning until there no more shows left.
|
|
||||||
ScanSeries(notification);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AutoIgnoreSeasons(int seriesId)
|
|
||||||
{
|
|
||||||
//Todo: Need to convert this over to ObjectDb
|
|
||||||
return;
|
|
||||||
var episodeFiles = _mediaFileService.GetFilesBySeries(seriesId);
|
|
||||||
|
|
||||||
if (episodeFiles.Count() != 0)
|
|
||||||
{
|
|
||||||
var seasons = _seasonRepository.GetSeasonNumbers(seriesId);
|
|
||||||
var currentSeasons = seasons.Max();
|
|
||||||
|
|
||||||
foreach (var season in seasons)
|
|
||||||
{
|
|
||||||
if (season != currentSeasons && !episodeFiles.Any(e => e.SeasonNumber == season))
|
|
||||||
{
|
|
||||||
_seasonService.SetIgnore(seriesId, season, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in new issue