15

私はXMLエンティティフレームワークの s をいじっています。実行時にプロパティを挿入できるタイプのエンティティを作成しようとしました。最初にDynamicEntity動的なオブジェクトを作成しました

public class DynamicEntity : DynamicObject
{
    Dictionary<string, object> dynamicMembers = new Dictionary<string, object>();

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        dynamicMembers[binder.Name] = value;
        return true;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (dynamicMembers.TryGetValue(binder.Name, out result))
        {
            return dynamicMembers.TryGetValue(binder.Name, out result);
        }

        result = "";
        return true;
    }
}

エンティティはこれから継承します

public partial class QUOTE_HOUSE : DynamicEntity

(そして、dbからデータを取得した後にプロパティを手動で設定するとうまくいくようです)。

したがって、プロパティを削除するこのメカニズムに基づいて、プロパティを XML に挿入する別のメカニズムを実行しようとしましたが、すべてがうまくいったようです (少なくとも、XML が正しくない場合に通常行われるマッピングで爆発することはありませんvar mappingCollection = new StorageMappingItemCollection(conceptualCollection, storageCollection, new[] {mappingXml.CreateReader()});)。

問題は、クエリを実行するとEFが爆発することです

エンティティ タイプ QUOTE_HOUSE は、現在のコンテキストのモデルの一部ではありません。

説明: 現在の Web 要求の実行中に未処理の例外が発生しました。エラーの詳細とコード内のどこでエラーが発生したかについては、スタック トレースを確認してください。

例外の詳細: System.InvalidOperationException: エンティティ タイプ QUOTE_HOUSE は、現在のコンテキストのモデルの一部ではありません。

[InvalidOperationException: エンティティ タイプ QUOTE_HOUSE は、現在のコンテキストのモデルの一部ではありません。]
System.Data.Entity.Internal.InternalContext.UpdateEntitySetMappingsForType(Type entityType) +208
System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType ) ) +50

pdbをロードした後TryUpdateEntitySetMappingsForTypeにトレースしたものSystem.Data.Entity.Internal.InternalContextEF

基本的に何が起こるかQUOTE_HOUSEは、this._workspace.GetItemCollection(DataSpace.OSpace)どこUpdateEntitySetMappingsからマッピングしようとするかではありません。

それが入っているかどうかをチェックし、そうではないので、アイテムが存在しない場所this._entitySetMappingsCache.ContainsKey(entityType))を繰り返しマッピングの更新を試みますthis._workspace.GetItemCollection(DataSpace.OSpace)

ただし、エンティティが に存在することがわかりますthis._workspace.GetItems<EntityContainer>(DataSpace.CSpace)

完全なUpdateEntitySetMappings外観は次のとおりです。

private void UpdateEntitySetMappings()
{
  ObjectItemCollection objectItemCollection = (ObjectItemCollection) this._workspace.GetItemCollection(DataSpace.OSpace);
  ReadOnlyCollection<EntityType> items = this._workspace.GetItems<EntityType>(DataSpace.OSpace);
  Stack<EntityType> entityTypeStack = new Stack<EntityType>();
  foreach (EntityType entityType1 in items)
  {
    entityTypeStack.Clear();
    EntityType cspaceType = (EntityType) this._workspace.GetEdmSpaceType((StructuralType) entityType1);
    do
    {
      entityTypeStack.Push(cspaceType);
      cspaceType = (EntityType) cspaceType.BaseType;
    }
    while (cspaceType != null);
    EntitySet entitySet = (EntitySet) null;
    while (entitySet == null && entityTypeStack.Count > 0)
    {
      cspaceType = entityTypeStack.Pop();
      foreach (EntityContainer entityContainer in this._workspace.GetItems<EntityContainer>(DataSpace.CSpace))
      {
        List<EntitySetBase> list = entityContainer.BaseEntitySets.Where<EntitySetBase>((Func<EntitySetBase, bool>) (s => s.ElementType == cspaceType)).ToList<EntitySetBase>();
        int count = list.Count;
        if (count > 1 || count == 1 && entitySet != null)
          throw Error.DbContext_MESTNotSupported();
        if (count == 1)
          entitySet = (EntitySet) list[0];
      }
    }
    if (entitySet != null)
    {
      EntityType entityType2 = (EntityType) this._workspace.GetObjectSpaceType((StructuralType) cspaceType);
      Type clrType1 = objectItemCollection.GetClrType((StructuralType) entityType1);
      Type clrType2 = objectItemCollection.GetClrType((StructuralType) entityType2);
      this._entitySetMappingsCache[clrType1] = new EntitySetTypePair(entitySet, clrType2);
    }
  }
}

エンティティはどのようにして this._workspace.GetItemCollection(DataSpace.OSpace) に入りますか? 実体が にあるのに にCSpaceないのはなぜOSpaceですか?

編集: 報奨金でクラックしたい人のために、問題を再現するために環境をセットアップする必要があるかもしれないコンポーネントを以下に示します。

public class SystemToDatabaseMapping
{
    public SystemToDatabaseMapping(string system, string databaseType, string database, string connectionString, Type enitityType)
    {
        System = system;
        Database = database;
        DatabaseType = databaseType;
        ConnectionString = connectionString;
        EntityType = enitityType;
    }

    public Type EntityType { get; set; }
    public string System { get; set; }
    public string Database { get; set; }
    public string DatabaseType { get; set; }
    public string ConnectionString { get; set; }
    public List<ColumnToModify> ColumnsToModify  { get; set; }
}

public abstract class ColumnToModify
{
    protected ColumnToModify(string table, string column)
    {
        Table = table;
        Column = column;
    }

    public string Table { get; set; }
    public string Column { get; set; }

    public abstract bool IsRemove{ get; }
}

public class ColumnToRemove : ColumnToModify
{
    public ColumnToRemove(string table, string column) : base(table, column)
    {
    }

    public override bool IsRemove
    {
        get { return true; }
    }
}

public class ColumnToAdd : ColumnToModify
{
    public ColumnToAdd(string table, string column, Type type) : base(table, column)
    {
        this.Type = type;
    }

    public override bool IsRemove
    {
        get { return false; }
    }

    public Type Type { get; set; }
}

最初にデータベースから生成されたエンティティ (DynamicEntityコードは上にあります)

public partial class QUOTE_HOUSE : DynamicEntity
{
    public long UNIQUE_ID { get; set; }
}

データベースの DbContext にはコンストラクターのオーバーロードが必要です

 public partial class EcomEntities : DbContext
 {

    public EcomEntities(DbConnection connectionString)
        : base(connectionString, false)
    {
    }

    public virtual DbSet<QUOTE_HOUSE > QUOTE_HOUSE { get; set; }
....
}

列注入を行うメカニズム (これは大まかなプロトタイプなので、見栄えが悪いのは許してください)、try string 列を注入すると、問題なくマップされることがわかります。

public static class EntityConnectionExtensions
{
    public static IEnumerable<XElement> ElementsAnyNS<T>(this IEnumerable<T> source, string localName)
        where T : XContainer
    {
        return source.Elements().Where(e => e.Name.LocalName == localName);
    }

    public static IEnumerable<XElement> ElementsAnyNS(this XContainer source, string localName)
    {
        return source.Elements().Where(e => e.Name.LocalName == localName);
    }

    private static void ModifyNodes(XElement element, List<ColumnToModify> tableAndColumn)
    {
        if (element.Attribute("Name") != null && tableAndColumn.Any(oo => oo.Table == element.Attribute("Name").Value) ||
            element.Attribute("StoreEntitySet") != null && tableAndColumn.Any(oo => oo.Table == element.Attribute("StoreEntitySet").Value))
        {
            var matchingRemoveSelectParts = tableAndColumn.Where(oo => oo.IsRemove && element.Value.Contains(string.Format("\"{0}\".\"{1}\" AS \"{1}\"", oo.Table, oo.Column))).ToList();

            if (matchingRemoveSelectParts.Any())
            {
                foreach (var matchingRemoveSelectPart in matchingRemoveSelectParts)
                {
                    var definingQuery = element.ElementsAnyNS("DefiningQuery").Single();
                    definingQuery.Value = definingQuery.Value.Replace(string.Format(", \n\"{0}\".\"{1}\" AS \"{1}\"", matchingRemoveSelectPart.Table, matchingRemoveSelectPart.Column), "");
                }
            }
            else
            {
                var nodesToRemove = element.Nodes()
                    .Where(o =>
                        o is XElement
                        && ((XElement) o).Attribute("Name") != null
                        && tableAndColumn.Any(oo => oo.IsRemove && ((XElement) o).Attribute("Name").Value == oo.Column));

                foreach (var node in nodesToRemove.ToList())
                {
                    node.Remove();
                }

                if (element.Attribute("Name") != null && tableAndColumn.Any(oo => oo.Table == element.Attribute("Name").Value))
                {
                    var elementsToAdd = tableAndColumn.Where(o => !o.IsRemove && o.Table == element.Attribute("Name").Value);
                    if (new[] {"Type=\"number\"", "Type=\"varchar2\"", "Type=\"date\""}.Any(o => element.ToString().Contains(o)))
                    {
                        foreach (var columnToModify in elementsToAdd)
                        {
                            var columnToAdd = (ColumnToAdd) columnToModify;

                            var type = new[] {typeof (decimal), typeof (float), typeof (int), typeof (bool)}.Contains(columnToAdd.Type)
                                ? "number"
                                : columnToAdd.Type == typeof (DateTime) ? "date" : "varchar2";

                            var precision = "";
                            var scale = "";
                            var maxLength = "";
                            if (type == "number")
                            {
                                precision = "38";
                                scale = new[] {typeof (decimal), typeof (float)}.Contains(columnToAdd.Type) ? "2" : "0";
                            }

                            if (type == "varchar2")
                            {
                                maxLength = "500";
                            }

                            var newProperty = new XElement(element.GetDefaultNamespace() + "Property", new XAttribute("Name", columnToAdd.Column), new XAttribute("Type", type));
                            if (!string.IsNullOrWhiteSpace(precision))
                            {
                                newProperty.Add(new XAttribute("Precision", precision));
                            }

                            if (!string.IsNullOrWhiteSpace(scale))
                            {
                                newProperty.Add(new XAttribute("Scale", scale));
                            }

                            if (!string.IsNullOrWhiteSpace(maxLength))
                            {
                                newProperty.Add(new XAttribute("MaxLength", maxLength));
                            }

                            element.Add(newProperty);
                        }
                    }
                    else if (
                        new[] {"Type=\"Decimal\"", "Type=\"String\"", "Type=\"DateTime\"", "Type=\"Boolean\"", "Type=\"Byte\"", "Type=\"Int16\"", "Type=\"Int32\"", "Type=\"Int64\""}.Any(
                            o => element.ToString().Contains(o)))
                    {
                        foreach (var columnToModify in elementsToAdd)
                        {
                            var columnToAdd = (ColumnToAdd) columnToModify;

                            var type = new[] {typeof (decimal), typeof (float), typeof (int), typeof (bool)}.Contains(columnToAdd.Type)
                                ? "Decimal"
                                : columnToAdd.Type == typeof (DateTime) ? "DateTime" : "String";

                            var precision = "";
                            var scale = "";
                            var maxLength = "";
                            if (type == "Decimal")
                            {
                                precision = "38";
                                scale = new[] {typeof (decimal), typeof (float)}.Contains(columnToAdd.Type) ? "2" : "0";
                            }

                            if (type == "String")
                            {
                                maxLength = "500";
                            }

                            var newProperty = new XElement(element.GetDefaultNamespace() + "Property", new XAttribute("Name", columnToAdd.Column), new XAttribute("Type", type));
                            if (!string.IsNullOrWhiteSpace(precision))
                            {
                                newProperty.Add(new XAttribute("Precision", precision));
                            }

                            if (!string.IsNullOrWhiteSpace(scale))
                            {
                                newProperty.Add(new XAttribute("Scale", scale));
                            }

                            if (!string.IsNullOrWhiteSpace(maxLength))
                            {
                                newProperty.Add(new XAttribute("MaxLength", maxLength));
                                newProperty.Add(new XAttribute("FixedLength", "false"));
                                newProperty.Add(new XAttribute("Unicode", "false"));
                            }

                            element.Add(newProperty);
                        }
                    }
                }
            }

            if (element.Attribute("Name") != null && tableAndColumn.Any(oo => oo.Table == element.Attribute("Name").Value) && element.GetNamespaceOfPrefix("store") != null &&
                element.Attribute(element.GetNamespaceOfPrefix("store") + "Type") != null &&
                element.Attribute(element.GetNamespaceOfPrefix("store") + "Type").Value == "Tables")
            {
                var matchingAddSelectParts = tableAndColumn.Where(o => !o.IsRemove && o.Table == element.Attribute("Name").Value);
                foreach (var matchingAddSelectPart in matchingAddSelectParts)
                {
                    var definingQuery = element.ElementsAnyNS("DefiningQuery").Single();
                    var schemaRegex = new Regex(string.Format("\\nFROM \\\"([a-zA-Z0-9]*)\\\".\\\"{0}\\\"", matchingAddSelectPart.Table));
                    var schema = schemaRegex.Matches(definingQuery.Value)[0].Groups[1].Value;
                    definingQuery.Value = definingQuery.Value.Replace(
                        string.Format("\nFROM \"{0}\".\"{1}\" \"{1}\"", schema, matchingAddSelectPart.Table),
                        string.Format(", \n\"{0}\".\"{1}\" AS \"{1}\"\nFROM \"{2}\".\"{0}\" \"{0}\"", matchingAddSelectPart.Table, matchingAddSelectPart.Column, schema));
                }
            }

            if (element.Attribute("StoreEntitySet") != null && tableAndColumn.Any(oo => !oo.IsRemove && oo.Table == element.Attribute("StoreEntitySet").Value))
            {
                var matchingAddSelectParts = tableAndColumn.Where(o => !o.IsRemove && o.Table == element.Attribute("StoreEntitySet").Value);
                foreach (var matchingAddSelectPart in matchingAddSelectParts)
                {
                    element.Add(new XElement(element.GetDefaultNamespace() + "ScalarProperty", new XAttribute("Name", matchingAddSelectPart.Column),
                        new XAttribute("ColumnName", matchingAddSelectPart.Column)));
                }
            }
        }
    }

    public static EntityConnection Create(List<ColumnToModify> tablesAndColumns, string connString)
    {
        var modelNameRegex = new Regex(@".*metadata=res:\/\/\*\/([a-zA-Z.]*).csdl|.*");
        var model = modelNameRegex.Matches(connString).Cast<Match>().SelectMany(o => o.Groups.Cast<Group>().Skip(1).Where(oo => oo.Value != "")).Select(o => o.Value).First();

        var conceptualReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".csdl"));
        var mappingReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".msl"));
        var storageReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".ssdl"));

        var conceptualXml = XElement.Load(conceptualReader);
        var mappingXml = XElement.Load(mappingReader);
        var storageXml = XElement.Load(storageReader);

        foreach (var entitySet in new[] {storageXml, conceptualXml}.SelectMany(xml => xml.Elements()))
        {
            if (entitySet.Attribute("Name").Value == "ModelStoreContainer")
            {
                foreach (var entityContainerEntitySet in entitySet.Elements())
                {
                    ModifyNodes(entityContainerEntitySet, tablesAndColumns);
                }
            }

            ModifyNodes(entitySet, tablesAndColumns);
        }

        foreach (var entitySet in mappingXml.Elements().ElementAt(0).Elements())
        {
            if (entitySet.Name.LocalName == "EntitySetMapping")
            {
                foreach (var entityContainerEntitySet in entitySet.Elements().First().Elements())
                {
                    ModifyNodes(entityContainerEntitySet, tablesAndColumns);
                }
            }

            ModifyNodes(entitySet, tablesAndColumns);
        }

        var storageCollection = new StoreItemCollection(new [] {storageXml.CreateReader()});
        var conceptualCollection = new EdmItemCollection(new[] { conceptualXml.CreateReader() });
        var mappingCollection = new StorageMappingItemCollection(conceptualCollection, storageCollection, new[] {mappingXml.CreateReader()});

        var workspace = new MetadataWorkspace();

        workspace.RegisterItemCollection(conceptualCollection);
        workspace.RegisterItemCollection(storageCollection);
        workspace.RegisterItemCollection(mappingCollection);
        var connectionData = new EntityConnectionStringBuilder(connString);
        var connection = DbProviderFactories
            .GetFactory(connectionData.Provider)
            .CreateConnection();
        connection.ConnectionString = connectionData.ProviderConnectionString;

        return new EntityConnection(workspace, connection);
    }
}

初期化:

public ActionResult QUOTE_HOUSE()
    {
        var onlineDocs = Enumerable.Empty<QUOTE_HOUSE>();
        var mappings = new List<SagaSystemToDatabaseMapping>{new SagaSystemToDatabaseMapping("x", "Oracle", "Db1",
                   "metadata=res://*/Ecom.Ecom.csdl|res://*/Ecom.Ecom.ssdl|res://*/Ecom.Ecom.msl;provider=Oracle.ManagedDataAccess.Client;provider connection string='...'", typeof(EcomEntities))
                {
                    ColumnsToModify = new List<ColumnToModify> { new ColumnToAdd("QUOTE_HOUSE","TESTCOL", typeof(string))    }
                }};
        var entityConnection = EntityConnectionExtensions.Create(mappings[0].ColumnsToModify,mappings[0].ConnectionString);
        using (var db = new EcomEntities(entityConnection))
        {
            onlineDocs = db.QUOTE_HOUSE.Take(10);
        }

        return View("QUOTE_HOUSE", onlineDocs.ToList());
    }

QUOTE_HOUSEエンティティから oracle データベースを生成し、いくつかのダミー値を入力できるはずです.ToList()。データベースを生成した後、データベースに追加の列を追加しますが、モデルには追加しません ( alter table QUOTE_HOUSE add TESTCOL Varchar2(20)) - 実行時にモデルに挿入される列をデータベースに追加します。また、 EF アセンブリをデバッグする必要がある場合もあります。これを行う方法は次のとおりです。さらに情報が必要な場合、または何か見逃している場合はお知らせください。

4

1 に答える 1

9

これはおそらくあなたが期待しているものではないことはわかっていますが、少なくともその方向に時間を無駄にしないようにするのに役立つと思います.

幸いなことに、問題の原因は「ハック」コードではありません。実際、そのコードを使用せずに問題を再現できました。QUOTE_HOUSEを含むテーブルを作成し、TestColそれを新しい edmx コンテキストにインポートし、生​​成されたTestColプロパティをエンティティ クラスから削除しました。次に、クラスに を継承させDynamicEntity、デフォルトのコンストラクターを使用してコンテキストを作成し、呼び出されcontext.QUOTE_HOUSE.ToList()たところ、まったく同じ例外で爆発しました。

悪いニュースは、あなたが達成しようとしていることが不可能だということです。EF は、"オブジェクト スペース" メンバーのマッピングにリフレクションを使用します。インスタンスや動的ランタイムなどの型拡張メカニズムは提供しませTypeDescriptorん (動的オブジェクトに投影することさえできません)。すべての動的オブジェクトは異なるプロパティを持つ可能性があり、動的などはないため、後者は理解できます。実行時に列を「削除」するトリックは、NotMapped最初にコードで使用するのと基本的に同じであるため、機能することに注意してください。つまり、プロパティは実際に存在しますが、EF によって無視されます。

CSpaceエンティティが にあるのに にない理由に興味がある場合OSpace、答えはOSpaceTypeFactory(内部System.Data.Entity.Core.Metadata.Edm名前空間) - sourceと呼ばれる内部クラスに含まれています。というメソッドがありTryCreateStructuralType、それが を呼び出しTryCreateMembersて を返すfalse場合、型は追加されません。TryCreateMembers次に を呼び出し、リフレクションを使用して抽出されTryFindAndCreatePrimitivePropertiesたリストを渡します。オブジェクト プロパティにメンバーをマップできない場合は後で戻ります。これにより、型が型コレクション に追加されるのを効果的に防ぎます。PropertyInfofalse CSpaceOSpaceOSpace

少なくともそれであなたの好奇心が満たされることを願っています:)しかし、実行時にEFエンティティにプロパティを「追加」することは、残念ながら死んだ考えです。

于 2016-07-02T18:15:15.897 に答える