13

Entity Framework 5 (Code First アプローチ) を使用して、従来のストアド プロシージャから自分のクラスにパラメーターを設定していますが、これは正常に機能しています (詳細は後述)。私の問題は、列の名前を異なる名前のプロパティにマップしたいということです (Erp からの名前は好きではありません)。別の名前のプロパティの列名を指定するために、構成クラス (ビューやテーブルにマップするときのように) を使用しようとしましたが、結果は次のとおりです。

  • 構成クラスを使用しない場合 (DbContext の OnModelCreating メソッドに追加しない場合)、EF は機能しますが、列の名前と正確に一致するプロパティのみを読み込みます (これは、これで期待したことです)場合); その他のプロパティは null です。
  • 構成クラスを使用する場合 (DbContext の OnModelCreating メソッドで modelBuilder に追加)、EF は、「データ リーダーは指定された '...Item' と互換性がありません。型のメンバー ' Description' には、データ リーダーに同じ名前の対応する列がありません」と、構成でプロパティ Description が列 ItemDescription にマップされるように指定しているため、これは非常に奇妙に思えます。

構成が結果に影響を与えているのに、その仕様が列のマッピングに使用されないのはなぜですか? SqlQuery を使用してこのマッピングを指定する別の方法はありますか?

詳細は次のとおりです。

私のPOCOクラス:

public class Item
    {
        public String Id { get; set; }
        public String Description { get; set; }
    }

構成クラス:

public class ItemConfiguration : EntityTypeConfiguration<Item>
    {
        public ItemConfiguration()
        {
            HasKey(x => new { x.Id });
            Property(x => x.Id).HasColumnName("Code");
            Property(x => x.Description).HasColumnName("ItemDescription");
        }
    }

ストアド プロシージャは、列 "Code" および "ItemDescription" を含むデータを返します。私はこのように呼んでいます:

var par = new SqlParameter();
par.ParameterName = "@my_par";
par.Direction = ParameterDirection.Input;
par.SqlDbType = SqlDbType.VarChar;
par.Size = 20;
par.Value = ...;

var data = _context.Database.SqlQuery<Item>("exec spItem @my_par", par);

これで、構成をコンテキストに追加します。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
      modelBuilder.Configurations.Add(new ItemConfiguration());
}

ありがとうございました!

4

4 に答える 4

11

ここで見つけました:

http://entityframework.codeplex.com/workitem/233?PendingVoteId=233

その「SqlQueryメソッドは、マッピングを考慮しないように設計されています...」。

彼らはまた、「SqlQuery が列属性を尊重するようにするオプションがあると便利であることに同意するので、この問題を未解決のままにして、将来の検討のためにバックログに入れています。」と言っているので、同じ問題がある場合は、投票してください:-)

于 2013-02-07T10:56:36.733 に答える
3

その間、この方法を使用できます。テストはほとんどありませんが (私のクラスでは機能したため)、必要に応じて修正するのは難しくありません... コンテキストが必要であり (マップされたカスタム型を取得するため)、同時にデータリーダーを実行するには別の接続が必要です。

使用法:
生徒のリスト = Mapper.Map(context, (new SchoolContext()).Database.Connection, "Select * from Students");

public static class Mapper
{
    /// <summary>
    /// Maps the result of a query into entities.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="context">The context.</param>
    /// <param name="queryConnection">The connection to run the query. Must be different from the one on the context.</param>
    /// <param name="sqlQuery">The SQL query.</param>
    /// <returns>An entity list</returns>
    /// <exception cref="System.ArgumentNullException">
    /// context
    /// or
    /// queryConnection
    /// or
    /// sqlQuery
    /// </exception>
    public static List<T> Map<T>(DbContext context, DbConnection queryConnection, string sqlQuery) where T:new()
    {
        if (context == null) 
            throw new ArgumentNullException("context");
        if (queryConnection == null)
            throw new ArgumentNullException("queryConnection");
        if (sqlQuery == null) 
            throw new ArgumentNullException("sqlQuery");

        var connectionState = queryConnection.State;

        if (connectionState != ConnectionState.Open)
            queryConnection.Open();

        DbCommand command = queryConnection.CreateCommand();
        command.CommandText = sqlQuery;
        DbDataReader reader = command.ExecuteReader();

        List<T> entities = new List<T>();

        while (reader.Read())
        {
            entities.Add(InternalMap<T>(context, reader));
        }

        if (connectionState != ConnectionState.Open)
            queryConnection.Close();

        return entities;

    }

    private static T InternalMap<T>(DbContext context, DbDataReader reader) where T: new()
    {

        T entityObject = new T();

        InternalMapEntity(context, reader, entityObject);

        return entityObject;
    }

    private static void InternalMapEntity(DbContext context, DbDataReader reader, object entityObject)
    {

        ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
        var metadataWorkspace = ((EntityConnection)objectContext.Connection).GetMetadataWorkspace();

        IEnumerable<EntitySetMapping> entitySetMappingCollection = metadataWorkspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace).Single().EntitySetMappings;
        IEnumerable<AssociationSetMapping> associationSetMappingCollection = metadataWorkspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace).Single().AssociationSetMappings;

        var entitySetMappings = entitySetMappingCollection.First(o => o.EntityTypeMappings.Select(e => e.EntityType.Name).Contains(entityObject.GetType().Name));

        var entityTypeMapping = entitySetMappings.EntityTypeMappings[0];
        string tableName = entityTypeMapping.EntitySetMapping.EntitySet.Name;
        Console.WriteLine(tableName);

        MappingFragment mappingFragment = entityTypeMapping.Fragments[0];

        foreach (PropertyMapping propertyMapping in mappingFragment.PropertyMappings)
        {
            object value = Convert.ChangeType(reader[((ScalarPropertyMapping) propertyMapping).Column.Name], propertyMapping.Property.PrimitiveType.ClrEquivalentType);
            entityObject.GetType().GetProperty(propertyMapping.Property.Name).SetValue(entityObject, value, null);
            Console.WriteLine("{0} {1} {2}", propertyMapping.Property.Name, ((ScalarPropertyMapping)propertyMapping).Column, value);
        }

        foreach (var navigationProperty in entityTypeMapping.EntityType.NavigationProperties)
        {
            PropertyInfo propertyInfo = entityObject.GetType().GetProperty(navigationProperty.Name);

            AssociationSetMapping associationSetMapping = associationSetMappingCollection.First(a => a.AssociationSet.ElementType.FullName == navigationProperty.RelationshipType.FullName);

            // associationSetMapping.AssociationTypeMapping.MappingFragment.PropertyMappings contains two elements one for direct and one for inverse relationship
            EndPropertyMapping propertyMappings = associationSetMapping.AssociationTypeMapping.MappingFragment.PropertyMappings.Cast<EndPropertyMapping>().First(p => p.AssociationEnd.Name.EndsWith("_Target"));

            object[] key = propertyMappings.PropertyMappings.Select(c => reader[c.Column.Name]).ToArray();
            object value = context.Set(propertyInfo.PropertyType).Find(key);
            propertyInfo.SetValue(entityObject, value, null);
        }

    }
}
于 2015-06-11T10:07:14.077 に答える
0

何年か 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);
                }
            }
        }
    }
于 2019-08-15T10:41:44.610 に答える