经过几年使用 bubi 的方法并实现了一些代码,我决定在这里发布我们的改进。请注意,我不会发布对其他命名空间的引用。只需根据您的需要进行调整。
无论如何,我希望它可以帮助某人。
使用系统;
使用 System.Collections.Generic;
使用 System.Collections.Immutable;
使用 System.Data;
使用 System.Data.Common;
使用 System.Data.Entity.Core.EntityClient;
使用 System.Data.Entity.Core.Mapping;
使用 System.Data.Entity.Core.Metadata.Edm;
使用 System.Data.Entity.Infrastructure;
使用 System.Diagnostics;
使用 System.Linq;
使用 System.Linq.Expressions;
公共抽象部分类 BaseService
其中 TEntity : EntityDefault
{
私有常量 int MAX_ITEMS_PER_PREDICATE = 500;
///
/// Lista imutável Competitiono todos os predicates, por tipo da entidade, a serem buscados no banco de dados。
///
私有 ImmutableDictionary> 谓词 { 获取;放; }
私有 ImmutableDictionary PredicatesCount { get; 放; }
私有 ImmutableDictionary> LoadedPredicates { get; 放; }
///
/// Lista imutável 竞争作为 entidades,que são propriedades de navegação,já buscadas no banco de dados。
///
私有 ImmutableList NavigationEntities { 获取;放; }
///
/// Lista imutável opinion todas as propriedades de navegação
///
私有 ImmutableList NavigationProperties { get; 放; }
///
/// 将查询结果映射到实体中。
///
///
/// SQL 查询。
/// 传递给过程的参数列表
///
/// 当查询为空或为空时,它可能返回空。
/// 一个实体列表
///
/// 语境
/// 或者
/// 查询连接
/// 或者
/// sql查询
///
公共列表 SqlQuery(字符串查询,字典参数,参数 KeyValuePair[] 选项)其中 T:EntityDefault
{
DbConnection queryConnection = null;
尝试
{
InitOrResetSqlQueryVariables();
如果(查询.HasntValue())
{
抛出新的 ArgumentNullException(nameof(query));
}
queryConnection = Db.Database.Connection;
var connectionState = queryConnection.State;
如果(连接状态!=连接状态。打开)
{
查询连接.Open();
}
var command = queryConnection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = 查询;
如果(参数!= null)
{
command.AddParameters(参数);
}
var reader = command.ExecuteReader();
var 实体 = 新列表();
而(读者。读())
{
entity.Add(MapEntity(reader));
}
LoadNavigationProperties(实体,选项);
返回实体;
}
最后
{
InitOrResetSqlQueryVariables();
if (Db.BaseDb.AutoCloseConnection && queryConnection != null)
{
if (queryConnection.State != ConnectionState.Closed)
{
查询连接.关闭();
}
queryConnection.Dispose();
}
}
}
public List SqlQuery(string query, List parameters, params KeyValuePair[] options) where T : EntityDefault
{
DbConnection queryConnection = null;
尝试
{
InitOrResetSqlQueryVariables();
如果(查询.HasntValue())
{
抛出新的 ArgumentNullException(nameof(query));
}
queryConnection = Db.Database.Connection;
var connectionState = queryConnection.State;
如果(连接状态!=连接状态。打开)
{
查询连接.Open();
}
var command = queryConnection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = 查询;
如果(参数!= null)
{
command.Parameters.AddRange(parameters.ToArray());
}
var reader = command.ExecuteReader();
var 实体 = 新列表();
而(读者。读())
{
entity.Add(MapEntity(reader));
}
LoadNavigationProperties(实体,选项);
返回实体;
}
最后
{
InitOrResetSqlQueryVariables();
if (Db.BaseDb.AutoCloseConnection && queryConnection != null)
{
if (queryConnection.State != ConnectionState.Closed)
{
查询连接.关闭();
}
queryConnection.Dispose();
}
}
}
私人 T MapEntity(IDataRecord 阅读器)
{
var entityObject = Activator.CreateInstance();
MapEntity(reader, entityObject);
返回实体对象;
}
private void MapEntity(IDataRecord reader, object entityObject)
{
var objectContext = ((IObjectContextAdapter)Db).ObjectContext;
var metadataWorkspace = ((EntityConnection)objectContext.Connection).GetMetadataWorkspace();
var entitySetMappingCollection =
metadataWorkspace.GetItems(DataSpace.CSSpace).Single().EntitySetMappings;
var associationSetMappingCollection =
metadataWorkspace.GetItems(DataSpace.CSSpace)
。单身的()
.AssociationSetMappings.ToList();
var entitySetMappings =
entitySetMappingCollection.First(
o => o.EntityTypeMappings.Select(e => e.EntityType.Name).Contains(entityObject.GetType().Name));
var entityTypeMapping = entitySetMappings.EntityTypeMappings[0];
var tableName = entityTypeMapping.EntitySetMapping.EntitySet.Name;
Debug.WriteLine(tableName);
var mappingFragment = entityTypeMapping.Fragments[0];
// 映射实体本身的属性
foreach(mappingFragment.PropertyMappings 中的 var propertyMapping)
{
var valueBeforCasting = reader[((ScalarPropertyMapping)propertyMapping).Column.Name];
var value = valueBeforCasting 是 DBNull
? 空值
: propertyMapping.Property.IsEnumType
? Convert.ChangeType(valueBeforCasting,
类型(int))
: Convert.ChangeType(valueBeforCasting,
propertyMapping.Property.PrimitiveType.ClrEquivalentType);
entityObject.GetType()
.GetProperty(propertyMapping.Property.Name)
.SetValue(entityObject, value, null);
Debug.WriteLine("{0} {1} {2}", propertyMapping.Property.Name,
((ScalarPropertyMapping)propertyMapping).Column, value);
}
if (NavigationProperties.Count == 0)
{
NavigationProperties = NavigationProperties.AddRange(entityTypeMapping.EntityType.NavigationProperties);
}
// 映射相关的导航属性
foreach (在 NavigationProperties 中的 var navigationProperty)
{
var propertyInfo = entityObject.GetType().GetProperty(navigationProperty.Name);
// 待办事项:Por Marco em 26/11/2015
/*
* Verificar em QueryOptions (que neste momento não é passada para esta rotina) se foi solicitado Eager Loading desta navigationProperty。
* Caso negativo executar um "继续;"
*
* Isso ajudará a evitar Consultas desnecessárias ao banco de dados。
*/
var propertyType = propertyInfo.PropertyType;
var 关联集映射 =
关联SetMappingCollection.First(
a => a.AssociationSet.ElementType.FullName == navigationProperty.RelationshipType.FullName);
// AssociationSetMapping.AssociationTypeMapping.MappingFragment.PropertyMappings 包含两个元素,一个用于直接关系,一个用于反向关系
var 属性映射 =
AssociationSetMapping.AssociationTypeMapping.MappingFragment.PropertyMappings
.Cast().First(p => p.AssociationEnd.Name.EndsWith("_Target"));
var key = propertyMappings.PropertyMappings.Select(c => reader[c.Column.Name]).ToArray();
if (!key.Any() || key[0] 为 DBNull)
继续;
///////////////////////////////////////// ///////////////////////////////////////// ///////////////////////
// Monta o PredicateBuilder que será utilizado para trazer todas as entidades associadas solicitadas
var outerPredicate = typeof(PredicateBuilder).InvokeStaticGenericMethod(propertyType, "False");
if (!Predicates.ContainsKey(propertyType))
{
var predicatesList = new List { outerPredicate };
谓词 = Predicates.Add(propertyType, predicatesList);
LoadedPredicates = LoadedPredicates.Add(propertyType, new List());
PredicatesCount = PredicatesCount.Add(propertyType, 0);
}
var loadedPredicates = LoadedPredicates[propertyType];
if (loadedPredicates.All(p => p != Convert.ToInt32(key[0])))
{
loadPredicates.Add(Convert.ToInt32(key[0]));
BuildPredicate(propertyType, outerPredicate, Convert.ToInt32(key[0]));
}
///////////////////////////////////////// ///////////////////////////////////////// ////////////////////////
// Seta o Id como helper para a rotina LoadAssociatedEntities
var value = Activator.CreateInstance(propertyType);
var idProperty = propertyType.GetProperty("Id");
idProperty.SetValue(value, key[0]);
propertyInfo.SetValue(entityObject, value, null);
}
}
private void BuildPredicate(Type propertyType, object outerPredicate, int pkValue)
{
var 参数 = Expression.Parameter(propertyType, "p");
var property = Expression.Property(parameter, "Id");
var valueToCompare = Expression.Constant(pkValue);
var equalsExpression = Expression.Equal(property, valueToCompare);
var funcType = typeof(Func).MakeGenericType(propertyType, typeof(bool));
var lambdaExpression = Expression.Lambda(funcType, equalsExpression, 参数);
var predicateList = Predicates[propertyType];
var predicatesCount = PredicatesCount[propertyType];
if (predicatesCount % MAX_ITEMS_PER_PREDICATE == 0)
{
predicateList.Add(outerPredicate);
}
var predicate = predicateList.Last();
predicate = typeof(PredicateBuilder).InvokeStaticGenericMethod(propertyType, "Or", predicate, lambdaExpression);
predicateList[predicateList.Count - 1] = 谓词;
谓词计数++;
PredicatesCount = PredicatesCount.Replace(propertyType, predicatesCount);
}
///
/// Carrega 通过 EagerLoading 作为 entidades associadas solicitadas
///
/// 实体默认的具体说明
/// Lista de entidades que irão ter as entidades associadas carregadas
/// Array de Eager Loadings a serem carregados
私人无效LoadNavigationProperties(IReadOnlyList实体,
参数 KeyValuePair[] eagerLoadings) 其中 T : EntityDefault
{
foreach(谓词中的 var predicateItem)
{
var newEagerLoadings = 新列表>();
var newOptions =
急切加载
.Where(p => p.Key == QueryOptions.DefineInclude || p.Key == QueryOptions.DefineIncludes)
.ToList();
var predicateWhere = predicateItem;
// Loop em todas as propriedades de navegação de T que sejam do mesmo tipo do predicate.Key
// Esse loop terá alimentado newEagerLoadings com os valores adequados。
前锋 (
var navigationProperty in
NavigationProperties.Where(
p => 实体[0].GetType().GetProperty(p.Name).PropertyType == predicateWhere.Key))
{
新选项 =
newOptions.Where(p => p.Value.ToString().StartsWith(navigationProperty.Name)).ToList();
if (!newOptions.Any())
继续;
// ReSharper 禁用一次 LoopCanBeConvertedToQuery
foreach(newOptions 中的 var 选项)
{
if (!option.Value.ToString().Contains("."))
{
继续;
}
var newOption = Pairing.Of(option.Key,
option.Value.ToString()
.RemovePrefix(navigationProperty.Name + ".")
.RemovePrefix(navigationProperty.Name));
if (newOption.HasntValue() || newOption.Value.ToString().IsNullOrEmpty())
{
继续;
}
newEagerLoadings.Add(newOption);
}
}
var predicateList = predicateItem.Value;
var funcType = predicateItem.Value.First().InvokeMethod("Compile", true).GetType();
var newInstanceOfThis = GetInstanceOfService(funcType.GenericTypeArguments[0], Db);
foreach(predicateList 中的 var 谓词)
{
// A fim detentar evitar bugs de StackOverflow
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
var expandPredicate = typeof(Extensions).InvokeStaticGenericMethod(funcType, "Expand", predicate);
var selectResponse = (IEnumerable)newInstanceOfThis.InvokeGenericMethod(predicateItem.Key,
“很多”,expandedPredicate,newEagerLoadings.ToArray());
var listOfItems = selectResponse.ToList();
// Obtém o retorno
// 执行一个查询 e preenche PredicateEntities
NavigationEntities = NavigationEntities.AddRange(listOfItems);
}
}
// 循环 nas entidades para atribuir as entidades associadas
foreach(实体中的 var 实体)
{
// Loop nas propriedades de navegação, para listar as entidades associadas
foreach (在 NavigationProperties 中的 var navigationProperty)
{
// navigationProperty é a entidade associada que será atribuída 实体
var propertyInfo = entity.GetType().GetProperty(navigationProperty.Name);
var propertyType = propertyInfo.PropertyType;
var propertyValue = propertyInfo.GetValue(entity);
if (propertyValue == null)
{
继续;
}
var idPropertyInfo = propertyType.GetProperty("Id");
var keyValue = idPropertyInfo.GetValue(propertyValue);
如果(键值 == 空)
{
继续;
}
var key = Convert.ToInt32(keyValue);
// Pega a lista de entidades associadas que sejam do mesmo tipo da propriedade de navegação
var associatedEntitiesOfSameType = NavigationEntities.Where(p => p.GetType() == propertyType)
.ToList();
if (!associatedEntitiesOfSameType.Any())
{
// O usuário não solicitou EagerLoading dessa navigationProperty
继续;
}
// Busca a entidade associada pelo Id, alimentado em "InternalMapEntity"
var 关联实体实例 =
associatedEntitiesOfSameType.FirstOrDefault(
p => Convert.ToInt32(idPropertyInfo.GetValue(p)) == key);
如果(关联实体实例 == 空)
继续; // Não 本地化。Removida do banco de dados?
// Atribui a entidade associada a "entity"
propertyInfo.SetValue(entity, associatedEntityInstance);
}
}
}
}