/// <summary> /// Returns a query that identifies the entity. /// </summary> /// <typeparam name="TModel"></typeparam> /// <param name="entity"></param> /// <returns></returns> public static DataModelQuery <TModel> EntityToIdentifyingQuery <TModel>(TModel model) where TModel : DataModel { var query = new DataModelQuery <TModel>(); bool pkfound = false; int pkc = 0; if (model.EntityMappings.PrimaryKeyColumns.Length > 0) { foreach (var field_kvp in model.EntityMappings.FieldMappings) { DataModelColumnAttribute field = field_kvp.Value; if (field.IsPrimaryKey) { string memName = field_kvp.Key; pkfound = (model[memName] != null); if (pkfound) { query.WhereProperty[memName].IsEqualTo(model[memName]); pkc++; } } } } if (!pkfound || model.EntityMappings.PrimaryKeyColumns.Length != pkc) { foreach (var field_kvp in model.EntityMappings.FieldMappings) { string memName = field_kvp.Key; query.WhereProperty[memName].IsEqualTo(model[memName]); } } return(query); }
/// <summary> /// Loads the full object graph for the /// <see cref="DataModel"/> objects that are returned from the specified /// <paramref name="query"/>, within the specified database /// <paramref name="transactionContext"/>. /// </summary> /// <param name="query"></param> /// <param name="transactionContext"></param> /// <param name="depth"></param> /// <param name="loadedModels"> /// Used for keeping recursive loading from resulting /// in infinite loops. Evaluate each loaded item from /// a database result set against this collection; if /// there is a match, use the collection item, /// otherwise use the database loaded item, deep-load /// it, and add it to this collection. /// </param> /// <returns></returns> public virtual DataModelCollection <TModel> DeepLoadModels <TModel>( DataModelQuery <TModel> query, int?depth, DbTransaction transactionContext, List <DataModel> loadedModels) where TModel : DataModel { DataModelCollection <TModel> entities = LoadModels(query); DataModel e; bool hasForeignKey = false; if (entities.Count > 0) { e = entities[0]; foreach (var field_kvp in e.EntityMappings.FieldMappings) { DataModelColumnAttribute field = field_kvp.Value; if (field.IsForeignKey) { hasForeignKey = true; break; } } if (!hasForeignKey && (e.EntityMappings.ForeignModelMappings == null || e.EntityMappings.ForeignModelMappings.Count == 0)) { return(entities); } } for (int i = 0; i < entities.Count; i++) { if (depth == null || depth > 0) { TModel entity = entities[i]; if (hasForeignKey && !loadedModels.Contains(entity)) { foreach (var field_kvp in entity.EntityMappings.FieldMappings) { DataModelColumnAttribute field = field_kvp.Value; if (field.IsForeignKey && field.ForeignKeyMapping.AssignToMember != null) { LoadMember(entity, field, field_kvp.Key, transactionContext, loadedModels); loadedModels.Add(entity); } } } if (entity.EntityMappings.ForeignModelMappings != null && entity.EntityMappings.ForeignModelMappings.Count > 0 ) { DataModelQuery <TModel> query2 = EntityToIdentifyingQuery(entity); entity = (depth == null) ? DeepLoadModel(query2, transactionContext, loadedModels) : DeepLoadModel(query2, depth - 1, transactionContext, loadedModels); //loadedModels.Add(entity); entities[i] = entity; } } } return(entities); }
private void LoadMember( DataModel dataModel, DataModelColumnAttribute fieldMapping, string memberName, DbTransaction transactionContext, List <DataModel> loadedModels) { LoadMember(dataModel, null, fieldMapping, memberName, transactionContext, loadedModels); }
/// <summary> /// Saves the changes that were made to the /// specified <paramref name="dataModel"/>, within the specified /// database <paramref name="transactionContext"/>. /// <remarks> /// The specified object must have the Deleted property set to true, /// IsDirty evaluated as true, or IsNew evaluated as true, or else /// the entity will not be saved. /// </remarks> /// </summary> /// <param name="dataModel"></param> /// <param name="transactionContext"></param> public override void SaveModel <TModel>(TModel dataModel, DbTransaction transactionContext) { if (!dataModel.IsNew && !dataModel.IsDirty && !dataModel.MarkDeleted) { return; } DataModelMap demap = DataModelMap.GetEntityMapping(dataModel.GetType()); DbConnection tmpConn = null; if (transactionContext == null) { tmpConn = CreateAndOpenConnection(); transactionContext = tmpConn.BeginTransaction(); } try { DbDataProviderCommandBuilder <TModel> cmdBuilder = CreateCommandBuilder(dataModel); using (DbCommand cmd = cmdBuilder.CreateDbCommand(transactionContext)) { cmd.Transaction = transactionContext; bool wasNew = dataModel.IsNew; cmd.ExecuteNonQuery(); if (wasNew) { foreach (var field in demap.FieldMappings) { DataModelColumnAttribute f = field.Value; if (f.ReturnAsOutputOnInsert) { dataModel[f.TargetMember.Name] = cmd.Parameters[f.InsertParam].Value; } } } } } finally { if (tmpConn != null) { transactionContext.Commit(); tmpConn.Close(); } } }
private void LoadMember( DataModel dataModel, int?depth, DataModelColumnAttribute fieldMapping, string memberName, DbTransaction transactionContext, List <DataModel> loadedModels) { DataModelColumnAttribute field = fieldMapping; DataModel e = dataModel; ForeignKeyAttribute fkmapping = field.ForeignKeyMapping; string fMemberName = field.ForeignKeyMapping.AssignToMember; FieldInfo memField = e.Entity.GetType().GetField(fMemberName); PropertyInfo memProp = e.Entity.GetType().GetProperty(fMemberName); Type memberType = memField != null ? memField.FieldType : memProp.PropertyType; bool useAssignWrapper = false; if (!memberType.IsDataModel()) { useAssignWrapper = true; } Type dataMemberType; dataMemberType = useAssignWrapper ? typeof(DataModel <>).MakeGenericType(memberType) : memberType; Type queryType = typeof(DataModelQuery <>).MakeGenericType(dataMemberType); IDataModelQuery subquery = ((IDataModelQuery)Activator.CreateInstance(queryType)) .WhereProperty[fkmapping.ForeignEntityProperty].IsEqualTo(e[memberName]); if (depth != null) { depth = depth - 1; } DataModel subentity = DeepLoadModel(dataMemberType, subquery, depth, transactionContext, loadedModels); if (useAssignWrapper) { memProp.SetValue( e.Entity, subentity.Entity, new object[] {}); } else { memProp.SetValue(e, subentity, new object[] {}); } }
private string ValueToExpression(DataModelColumnAttribute fieldMetadata, object value) { return(ValueToExpression(fieldMetadata.DbType, value)); }
/// <summary> /// Returns a generated array of <see cref="DbParameter"/>s /// that should be passed in with the command. /// </summary> /// <returns></returns> protected virtual DbParameter[] CreateParameters() { // add insert/update values var ret = new List <DbParameter>(); foreach (var colmap_kvp in ModelMap.FieldMappings) { string fld = colmap_kvp.Key; DataModelColumnAttribute colmap = colmap_kvp.Value; DbParameter param = null; switch (StatementCommand) { case SqlStatement.INSERT: if (string.IsNullOrEmpty(colmap.InsertParam)) { continue; } if (colmap.IncludeOnInsert || colmap.ReturnAsOutputOnInsert) { param = Provider.DbFactory.CreateParameter(); param.ParameterName = !colmap.InsertParam.StartsWith("@") ? "@" + colmap.InsertParam : colmap.InsertParam; if (colmap.ReturnAsOutputOnInsert) { param.Direction = ParameterDirection.Output; } } break; case SqlStatement.UPDATE: if (string.IsNullOrEmpty(colmap.UpdateParam)) { continue; } bool add = true; if (!CommandBuilder.SetAllValues) { add = DataModel.ModifiedProperties.Contains(fld); } if (add) { param = Provider.DbFactory.CreateParameter(); param.ParameterName = !colmap.UpdateParam.StartsWith("@") ? "@" + colmap.UpdateParam : colmap.UpdateParam; } break; } if (param != null && ret.Find(p => p.ParameterName == param.ParameterName) == null) { if (param is SqlParameter) { ((SqlParameter)param).SqlDbType = colmap.SqlDbType; } else { param.DbType = colmap.DbType; } if (colmap.ColumnSize.HasValue) { param.Value = colmap.ColumnSize.Value; } else if (colmap.DataType == typeof(string)) { param.Size = (DataModel[fld] ?? string.Empty).ToString().Length; } if (param.Direction == ParameterDirection.Input || param.Direction == ParameterDirection.InputOutput) { param.IsNullable = colmap.IsNullable; param.Value = DataModel[fld] ?? DBNull.Value; } ret.Add(param); } } foreach (var foreignMapping_kvp in ModelMap.ForeignModelMappings) { ForeignDataModelAttribute foreignMapping = foreignMapping_kvp.Value; if (foreignMapping.TargetMemberType.IsOrInherits(typeof(IEnumerable))) { continue; } switch (StatementCommand) { case SqlStatement.INSERT: var fieldValue = DataModel.ColumnMappedValue[foreignMapping.LocalColumn]; var paramName = "@" + foreignMapping.LocalColumn; if (ret.Find(p => p.ParameterName == paramName) == null) { if (ret.Find(p => p.ParameterName == paramName) != null) { continue; } var param = Provider.DbFactory.CreateParameter(); param.ParameterName = paramName; if (param is SqlParameter) { ((SqlParameter)param).SqlDbType = foreignMapping.LocalColumnSqlDbType; } else { param.DbType = foreignMapping.LocalColumnDbType; } if (foreignMapping.LocalColumnSize.HasValue) { param.Value = foreignMapping.LocalColumnSize.Value; } else if (foreignMapping.TargetMemberType == typeof(string)) { param.Size = (fieldValue ?? string.Empty).ToString().Length; } if (param.Direction == ParameterDirection.Input || param.Direction == ParameterDirection.InputOutput) { param.IsNullable = foreignMapping.LocalColumnIsNullable; param.Value = fieldValue ?? DBNull.Value; } ret.Add(param); } break; } } // add query conditions if (Query != null) { foreach (DataModelQueryCondition <TModel> cond in Query.Conditions) { DataModelColumnAttribute fm = cond.FieldMap; if (fm == null) { switch (cond.FindFieldMappingBy) { case FieldMappingKeyType.ClrMember: fm = ModelMap[cond.EvalSubject]; break; case FieldMappingKeyType.DbColumn: fm = ModelMap.GetFieldMappingByDbColumnName(cond.EvalSubject); break; } } string paramName = string.Empty; if (fm != null) { switch (StatementCommand) { case SqlStatement.SELECT: if (string.IsNullOrEmpty(fm.SelectParam)) { continue; } paramName = fm.SelectParam; break; case SqlStatement.INSERT: if (string.IsNullOrEmpty(fm.InsertParam)) { continue; } paramName = fm.InsertParam; break; case SqlStatement.UPDATE: if (string.IsNullOrEmpty(fm.UpdateParam)) { continue; } paramName = fm.UpdateParam; break; case SqlStatement.DELETE: if (string.IsNullOrEmpty(fm.DeleteParam)) { continue; } paramName = fm.DeleteParam; break; } if (ret.Find(p => p.ParameterName == paramName) == null) { if (UsesSproc && cond.CompareOp != Compare.Equal) { throw new InvalidOperationException( "Cannot produce an ad hoc WHERE clause with a SQL Stored Procedure."); } DbParameter param = Provider.DbFactory.CreateParameter(); param.ParameterName = !paramName.StartsWith("@") ? "@" + paramName : paramName; if (param is SqlParameter) { ((SqlParameter)param).SqlDbType = fm.SqlDbType; } else { param.DbType = fm.DbType; } if (fm.ColumnSize.HasValue) { param.Value = fm.ColumnSize.Value; } else if (fm.DataType == typeof(string)) { fm.ColumnSize = (cond.CompareValue ?? string.Empty).ToString().Length; } param.IsNullable = fm.IsNullable; param.Value = cond.CompareValue ?? DBNull.Value; ret.Add(param); } } else { if (cond.CompareValue != null) { paramName = "@" + cond.EvalSubject; if (ret.Find(p => p.ParameterName == paramName) == null) { DbParameter param = Provider.DbFactory.CreateParameter(); param.ParameterName = paramName; if (param is SqlParameter) { ((SqlParameter)param).SqlDbType = DbTypeConverter.ToSqlDbType(cond.CompareValue); } else { param.DbType = DbTypeConverter.ToDbType(cond.CompareValue); } if (cond.CompareValue is string) { param.Size = ((string)cond.CompareValue).Length; } param.IsNullable = true; param.Value = cond.CompareValue; ret.Add(param); } } } } } return(ret.ToArray()); }
/// <summary> /// When implemented, loads the full object graph for the first /// <see cref="DataModel"/> that is returned from the specified /// <paramref name="query"/>, within the specified database /// <paramref name="transactionContext"/>. /// </summary> /// <param name="query"></param> /// <param name="transactionContext"></param> /// <param name="depth"></param> /// <param name="loadedModels"> /// Used for keeping recursive loading from resulting /// in infinite loops. Evaluate each loaded item from /// a database result set against this collection; if /// there is a match, use the collection item, /// otherwise use the database loaded item, deep-load /// it, and add it to this collection. /// </param> /// <returns></returns> public virtual TModel DeepLoadModel <TModel>( DataModelQuery <TModel> query, int?depth, DbTransaction transactionContext, List <DataModel> loadedModels ) where TModel : DataModel { TModel e = LoadModel(query, transactionContext); if (e == null) { return(e); } if (loadedModels == null) { loadedModels = new List <DataModel>(); } if (loadedModels.Contains(e)) { return((TModel)loadedModels[loadedModels.IndexOf(e)]); } foreach (DataModel previouslyLoadedModel in loadedModels) { if (query.GetType().IsGenericType) { Type qt = query.GetType().GetGenericArguments()[0]; if (previouslyLoadedModel.GetType().IsOrInherits(qt) && previouslyLoadedModel.Equals(e) && previouslyLoadedModel is TModel) { return((TModel)previouslyLoadedModel); } } } if (!loadedModels.Contains(e)) { loadedModels.Add(e); } foreach (var fe_kvp in e.EntityMappings.ForeignModelMappings) { if (depth == null || depth > 0) { ForeignDataModelAttribute fe = fe_kvp.Value; Type targetEntityType = fe.TargetMemberType; while (targetEntityType.IsGenericType && targetEntityType.IsOrInherits(typeof(IEnumerable))) { targetEntityType = targetEntityType.GetGenericArguments().Last(); } if (!targetEntityType.IsDataModel()) { targetEntityType = typeof(DataModel <>).MakeGenericType(targetEntityType); } Type subQueryType = typeof(DataModelQuery <>).MakeGenericType(targetEntityType); Relationship relationship = fe.Relationship; if (relationship == Relationship.ManyToMany && string.IsNullOrEmpty(fe.MappingTable)) { relationship = Relationship.OneToMany; } switch (relationship) { case Relationship.OneToOne: case Relationship.ManyToOne: IDataModelQuery subQuery = ((IDataModelQuery)Activator.CreateInstance(subQueryType)) .WhereColumn[fe.RelatedTableColumn].IsEqualTo( e.ColumnMappedValue[fe.LocalColumn]); DataModel e2 = DeepLoadModel(targetEntityType, subQuery, depth == null ? null : depth - 1, transactionContext, loadedModels); object e2o = e2; if (!fe.TargetMemberType.IsDataModel()) { e2o = (e2).Entity; } if (fe.TargetMember.MemberType == MemberTypes.Field) { ((FieldInfo)fe.TargetMember).SetValue(e.Entity, e2o); } else if (fe.TargetMember.MemberType == MemberTypes.Property) { ((PropertyInfo)fe.TargetMember).SetValue(e.Entity, e2o, new object[] {}); } break; case Relationship.OneToMany: IDataModelQuery subQuery2 = ((IDataModelQuery)Activator.CreateInstance(subQueryType)) .WhereColumn[fe.RelatedTableColumn].IsEqualTo( e.ColumnMappedValue[fe.LocalColumn]); IDataModelCollection e2c = DeepLoadModels(targetEntityType, subQuery2, depth, transactionContext, loadedModels); object e2ct = Activator.CreateInstance(fe.TargetMemberType); if (e2ct is IList) { bool de = fe.TargetMemberType.IsGenericType && fe.TargetMemberType.GetGenericArguments().Last().IsDataModel(); foreach (object e2cx in e2c) { if (de) { ((IList)e2ct).Add(e2cx); } else { object e2cx2 = ((DataModel)e2cx).Entity; ((IList)e2ct).Add(e2cx2); } } } else { e2ct = ((IList)e2c)[0]; } if (fe.TargetMember.MemberType == MemberTypes.Field) { ((FieldInfo)fe.TargetMember).SetValue(e.Entity, e2ct); } else if (fe.TargetMember.MemberType == MemberTypes.Property) { ((PropertyInfo)fe.TargetMember).SetValue(e.Entity, e2ct, new object[] {}); } break; case Relationship.ManyToMany: if (!fe.TargetMemberType.IsOrInherits(typeof(IList))) { throw new InvalidCastException( "Cannot apply ManyToMany binding to a non-IList property."); } Type tleft = fe.DeclaringType; Type tleftEntity = tleft; while (tleftEntity.IsDataModelWrapper(true)) { if (tleftEntity.BaseType != (typeof(DataModel <>)).BaseType) { tleftEntity = tleftEntity.BaseType; } else { tleftEntity = tleftEntity.GetGenericArguments()[0]; } } Type tright = (fe.TargetMemberType.IsGenericType && !fe.TargetMemberType.IsDataModel() && fe.TargetMemberType.IsOrInherits(typeof(IList))) ? fe.TargetMemberType.GetGenericArguments()[0] : fe.TargetMemberType; Type trightEntity = tright; if (!tright.IsDataModel()) { tright = typeof(DataModel <>).MakeGenericType(tright); } Type mapType = typeof(DataModelMap.RuntimeMappingTable <,>) .MakeGenericType( // left side of mapping table fe.TargetMember.DeclaringType, // right side tright); var mapObj = (DataModel)Activator.CreateInstance(mapType); mapObj.EntityMappings.TableMapping.Schema = fe.MappingTableSchema ?? ProviderDefaults.DefaultSchema; mapObj.EntityMappings.TableMapping.Table = fe.MappingTable ?? (string.Compare(trightEntity.Name, tleftEntity.Name) == -1 ? trightEntity.Name + tleftEntity.Name : tleftEntity.Name + trightEntity.Name); DataModelColumnAttribute mapLeftCol = mapObj.EntityMappings.FieldMappings["LeftColumn"]; mapLeftCol.ColumnName = fe.LocalColumn; mapLeftCol.DbType = e.EntityMappings .GetFieldMappingByDbColumnName(fe.LocalColumn).DbType; mapLeftCol.TargetMemberType = e.EntityMappings .GetFieldMappingByDbColumnName(fe.LocalColumn).TargetMemberType; Type mapQueryType = typeof(DataModelQuery <>).MakeGenericType(new[] { mapType }); var mapQuery = (IDataModelQuery)Activator.CreateInstance(mapQueryType); mapQuery.WhereColumn[fe.LocalColumn].IsEqualTo( e.ColumnMappedValue[fe.LocalColumn]); IDataModelCollection mapdes = LoadModels(mapType, mapQuery, transactionContext); var mappedDEs = new DataModelCollection <DataModel>(); foreach (DataModel de in mapdes) // de is a MappingTable<L,R> { var mappedDEQuery = (IDataModelQuery) Activator.CreateInstance(typeof(DataModelQuery <>) .MakeGenericType(targetEntityType)); mappedDEQuery.WhereColumn[fe.RelatedTableColumn] .IsEqualTo(de.ColumnMappedValue[fe.RelatedTableColumn]); DataModel mappedDE = DeepLoadModel(targetEntityType, mappedDEQuery, depth == null ? null : depth - 1, transactionContext, loadedModels); if (mappedDE != null) { mappedDEs.Add(mappedDE); } } Type mmtargtype = fe.TargetMemberType; var mmtargcol = (IList)Activator.CreateInstance(fe.TargetMemberType); Type mapdeType = null; foreach (DataModel mapde in mappedDEs) { if (mapdeType == null) { mapdeType = mapde.GetType(); } object deinst = mapde; if (mmtargtype.IsGenericType && !mmtargtype.GetGenericArguments()[0].IsDataModel()) { deinst = mapde.Entity; } mmtargcol.Add(deinst); } if (fe.TargetMember is FieldInfo) { ((FieldInfo)fe.TargetMember).SetValue(e, mmtargcol); } else if (fe.TargetMember is PropertyInfo) { ((PropertyInfo)fe.TargetMember).SetValue( e.Entity, mmtargcol, new object[] {}); } break; } } } foreach (var field_kvp in e.EntityMappings.FieldMappings) { DataModelColumnAttribute field = field_kvp.Value; if (field.IsForeignKey && field.ForeignKeyMapping.AssignToMember != null) { LoadMember(e, field, field_kvp.Key, transactionContext, loadedModels); } } return(e); }