何年か 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 の使用;
public 抽象部分クラス BaseService
ここで TEntity : EntityDefault
{
プライベート const int MAX_ITEMS_PER_PREDICATE = 500;
///
/// Lista imutável contendo todos os predicates, por tipo da entidade, a serem buscados no banco de dados.
///
プライベート ImmutableDictionary> 述語 { get; 設定; }
プライベート ImmutableDictionary PredicatesCount { get; 設定; }
プライベート ImmutableDictionary> LoadedPredicates {get; 設定; }
///
/// Lista imutável contendo as entidades, que são propriedades de navegação, já buscadas no banco de dados.
///
プライベート ImmutableList NavigationEntities { get; 設定; }
///
/// Lista imutável contendo todas as propriedades de navegação
///
プライベート ImmutableList NavigationProperties { get; 設定; }
///
/// クエリの結果をエンティティにマップします。
///
///
/// SQL クエリ。
/// プロシージャに渡されるパラメータのリスト
///
/// query が null または空の場合、null を返すことがあります。
/// エンティティ リスト
///
/// 環境
/// また
/// queryConnection
/// また
/// sqlQuery
///
public List SqlQuery(string query, Dictionary parameters, params KeyValuePair[] options) where T : EntityDefault
{
DbConnection queryConnection = null;
試す
{
InitOrResetSqlQueryVariables();
if (クエリ.HasntValue())
{
新しい ArgumentNullException(nameof(query)) をスローします。
}
queryConnection = Db.Database.Connection;
var connectionState = queryConnection.State;
if (connectionState != ConnectionState.Open)
{
queryConnection.Open();
}
var コマンド = queryConnection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = クエリ;
if (パラメータ != null)
{
command.AddParameters(パラメータ);
}
var リーダー = command.ExecuteReader();
var エンティティ = 新しい List();
while (reader.Read())
{
entities.Add(MapEntity(リーダー));
}
LoadNavigationProperties(エンティティ、オプション);
エンティティを返します。
}
最後に
{
InitOrResetSqlQueryVariables();
if (Db.BaseDb.AutoCloseConnection && queryConnection != null)
{
if (queryConnection.State != ConnectionState.Closed)
{
queryConnection.Close();
}
queryConnection.Dispose();
}
}
}
public List SqlQuery(string query, List parameters, params KeyValuePair[] options) where T : EntityDefault
{
DbConnection queryConnection = null;
試す
{
InitOrResetSqlQueryVariables();
if (クエリ.HasntValue())
{
新しい ArgumentNullException(nameof(query)) をスローします。
}
queryConnection = Db.Database.Connection;
var connectionState = queryConnection.State;
if (connectionState != ConnectionState.Open)
{
queryConnection.Open();
}
var コマンド = queryConnection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = クエリ;
if (パラメータ != null)
{
command.Parameters.AddRange(parameters.ToArray());
}
var リーダー = command.ExecuteReader();
var エンティティ = 新しい List();
while (reader.Read())
{
entities.Add(MapEntity(リーダー));
}
LoadNavigationProperties(エンティティ、オプション);
エンティティを返します。
}
最後に
{
InitOrResetSqlQueryVariables();
if (Db.BaseDb.AutoCloseConnection && queryConnection != null)
{
if (queryConnection.State != ConnectionState.Closed)
{
queryConnection.Close();
}
queryConnection.Dispose();
}
}
}
プライベート T MapEntity(IDataRecord リーダー)
{
var entityObject = Activator.CreateInstance();
MapEntity(リーダー、entityObject);
entityObject を返します。
}
private void MapEntity(IDataRecord リーダー、オブジェクト 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(テーブル名);
var mappingFragment = entityTypeMapping.Fragments[0];
// エンティティ自体のプロパティをマップします
foreach (mappingFragment.PropertyMappings 内の var propertyMapping)
{
var valueBeforCasting = reader[((ScalarPropertyMapping)propertyMapping).Column.Name];
var 値 = valueBeforCasting は DBNull です
? ヌル
: propertyMapping.Property.IsEnumType
? Convert.ChangeType(valueBeforCasting,
typeof(int))
: Convert.ChangeType(valueBeforCasting,
propertyMapping.Property.PrimitiveType.ClrEquivalentType);
entityObject.GetType()
.GetProperty(propertyMapping.Property.Name)
.SetValue(エンティティ オブジェクト、値、null);
Debug.WriteLine("{0} {1} {2}", propertyMapping.Property.Name,
((ScalarPropertyMapping)propertyMapping).Column, 値);
}
if (NavigationProperties.Count == 0)
{
NavigationProperties = NavigationProperties.AddRange(entityTypeMapping.EntityType.NavigationProperties);
}
// 関連するナビゲーション プロパティをマップします
foreach (NavigationProperties の var navigationProperty)
{
var propertyInfo = entityObject.GetType().GetProperty(navigationProperty.Name);
// TODO: 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 associationSetMapping =
AssociationSetMappingCollection.First(
a => a.AssociationSet.ElementType.FullName == navigationProperty.RelationshipType.FullName);
// associationSetMapping.AssociationTypeMapping.MappingFragment.PropertyMappings には、直接関係用と逆関係用の 2 つの要素が含まれています
var propertyMappings =
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() || キー[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 = 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])))
{
loadedPredicates.Add(Convert.ToInt32(key[0]));
BuildPredicate(propertyType, outerPredicate, Convert.ToInt32(key[0]));
}
/////////////////////////////////////////////// /////////////////////////////////////////////// ///////////////////////////
// Seta o Id como helper para a rotina LoadAssociatedEntities
var 値 = Activator.CreateInstance(propertyType);
var idProperty = propertyType.GetProperty("ID");
idProperty.SetValue(値、キー[0]);
propertyInfo.SetValue(entityObject、値、null);
}
}
private void BuildPredicate(Type propertyType, object outerPredicate, int pkValue)
{
var parameter = Expression.Parameter(propertyType, "p");
var property = Expression.Property(パラメータ, "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, parameter);
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 = PredicatesCount.Replace(propertyType, predicatesCount);
}
///
/// EagerLoading を介した entidades associadas solicitadas としての Carrega
///
/// EntityDefault の特殊なタイプ
/// Lista de entidades que irão ter as entidades associadas carregadas
/// Array de Eager Loadings a serem carregados
private void LoadNavigationProperties(IReadOnlyList エンティティ、
params KeyValuePair[]eagerLoadings) T : EntityDefault
{
foreach (Predicates の var predicateItem)
{
var newEagerLoadings = 新しいリスト>();
var newOptions =
熱心な読み込み
.Where(p => p.Key == QueryOptions.DefineInclude || p.Key == QueryOptions.DefineIncludes)
.ToList();
var predicateWhere = predicateItem;
// propriedades de navegação de T que sejam do mesmo tipo do predicate.Key として em todas をループします。
// Esse loop terá alimentado newEagerLoadings com os valores adequados.
foreach (
変数ナビゲーションのプロパティ
NavigationProperties.Where(
p => entities[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,
オプション.値.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 述語)
{
// 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
// クエリを実行して preenche PredicateEntities
NavigationEntities = NavigationEntities.AddRange(listOfItems);
}
}
// nas entidades para atribuir を 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 a entity
var propertyInfo = entity.GetType().GetProperty(navigationProperty.Name);
var propertyType = propertyInfo.PropertyType;
var propertyValue = propertyInfo.GetValue(エンティティ);
if (propertyValue == null)
{
継続する;
}
var idPropertyInfo = propertyType.GetProperty("ID");
var keyValue = idPropertyInfo.GetValue(propertyValue);
if (keyValue == null)
{
継続する;
}
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() == プロパティタイプ)
.ToList();
if (!associatedEntitiesOfSameType.Any())
{
// お願いします EagerLoading dessa navigationProperty
継続する;
}
// Busca a entidade associada pelo Id, alimentado em "InternalMapEntity"
var associatedEntityInstance =
associatedEntitiesOfSameType.FirstOrDefault(
p => Convert.ToInt32(idPropertyInfo.GetValue(p)) == キー);
if (associatedEntityInstance == null)
継続する; // ローカライズなし。Removida do banco de dados?
// Atribui a entidade associada a "entity"
propertyInfo.SetValue(エンティティ、関連するEntityInstance);
}
}
}
}