实体框架实体不在 DataSpace.OSpace (_workspace.GetItemCollection(DataSpace.OSpace)) 中,但在 DataSpace.CSpace 中 [英] Entity Framework entity is not in DataSpace.OSpace (_workspace.GetItemCollection(DataSpace.OSpace)) but is in DataSpace.CSpace

查看:17
本文介绍了实体框架实体不在 DataSpace.OSpace (_workspace.GetItemCollection(DataSpace.OSpace)) 中,但在 DataSpace.CSpace 中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在为实体框架处理 XMLs.我试图创建一种可以在运行时注入属性的实体类型,首先我创建了动态的 DynamicEntity 对象

I have been mucking around with XMLs for entity Framework. I tried to create a type of entity that could have properties injected at runtime, First I created DynamicEntity object that is dynamic

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;
    }
}

那么实体继承自这个

公共部分类 QUOTE_HOUSE : DynamicEntity

(当我从数据库获取数据后手动设置属性时,它似乎确实有效).

(and it does seem to work when I set properties manually after I get data from db).

所以基于这种机制删除属性 我尝试做另一个将属性插入到 XML 中的操作,并且整个事情似乎都没有问题(至少它不会在映射上爆炸,而当 XML 不正确时通常会这样做 varmappingCollection = new StorageMappingItemCollection(conceptualCollection, storageCollection, new[] {m​​appingXml.CreateReader()});).

so based on this mechanism of removing properties I tried to do another one that inserts properties into XMLs, and whole thing seems to hold up ok (at least it does not blow up on mapping which it usually does when XMLs are not right var mappingCollection = new StorageMappingItemCollection(conceptualCollection, storageCollection, new[] {mappingXml.CreateReader()});).

问题是 EF 执行查询时

Problem is EF when executing query blows up with

实体类型 QUOTE_HOUSE 不是当前上下文模型的一部分.

The entity type QUOTE_HOUSE is not part of the model for the current context.

描述:执行过程中发生未处理的异常当前的网络请求.请查看堆栈跟踪以了解更多信息有关错误及其在代码中的来源的信息.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

异常详细信息:System.InvalidOperationException:实体类型QUOTE_HOUSE 不是当前上下文模型的一部分.

Exception Details: System.InvalidOperationException: The entity type QUOTE_HOUSE is not part of the model for the current context.

[InvalidOperationException: 实体类型 QUOTE_HOUSE 不属于当前上下文的模型.]
System.Data.Entity.Internal.InternalContext.UpdateEntitySetMappingsForType(类型实体类型) +208
System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(类型实体类型) +50

[InvalidOperationException: The entity type QUOTE_HOUSE is not part of the model for the current context.]
System.Data.Entity.Internal.InternalContext.UpdateEntitySetMappingsForType(Type entityType) +208
System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType) +50

在为 EF

基本上发生的事情我的 QUOTE_HOUSE 不在 this._workspace.GetItemCollection(DataSpace.OSpace) 中,UpdateEntitySetMappings 试图从中映射它.

Basically what happens my QUOTE_HOUSE is not in this._workspace.GetItemCollection(DataSpace.OSpace) where UpdateEntitySetMappings tries to map it from.

它检查它是否在 this._entitySetMappingsCache.ContainsKey(entityType)) 中,因为它不是它然后尝试更新映射迭代 this._workspace.GetItemCollection(DataSpace.OSpace)代码>我的项目不存在

It checks if it's in this._entitySetMappingsCache.ContainsKey(entityType)) and since it's not it then tries update mappings iterating over this._workspace.GetItemCollection(DataSpace.OSpace) where my item doesn't exist

但是我可以看到我的实体确实存在于 this._workspace.GetItems(DataSpace.CSpace) 中.

However I can see that my entity does exist in 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 中?

How do entities get into this._workspace.GetItemCollection(DataSpace.OSpace)? Why would entity be in CSpace but not in OSpace ?

对于那些可能想要破解赏金的人,以下是您可能需要设置环境来重现问题的组件.

For those who might wanna have a crack at bounty, below are components you might need to set-up environment to reproduce the issue.

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; }
}

实体先从db生成,(DynamicEntity代码在上面)

Entity generated from db first, (DynamicEntity code is above)

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

数据库的 DbContext 需要构造函数重载

DbContext for database requires constructor overloads

 public partial class EcomEntities : DbContext
 {

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

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

进行列注入的机制(这是一个粗略的原型,因此请原谅它在 atm 中看起来有多糟糕),在注入 try 字符串列时,我知道它映射正常.

Mechanism that does column injection (it's a rough prototype so be forgiving to how bad it looks atm), when injecting try string column I know that it maps ok.

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(", 
"{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("
FROM "{0}"."{1}" "{1}"", schema, matchingAddSelectPart.Table),
                        string.Format(", 
"{0}"."{1}" AS "{1}"
FROM "{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添加 TESTCOL Varchar2(20)) - 数据库中的列在运行时注入模型中.您可能还需要调试 EF 程序集这是如何做到的.如果您需要更多信息或我遗漏了什么,请告诉我.

You should be able to generate oracle database from entity QUOTE_HOUSE and enter some dummy values, don't think you need a view as it blows up on .ToList(). After you generated the database add additional column to database but not model (alter table QUOTE_HOUSE add TESTCOL Varchar2(20)) - to have column in database that is being injected at runtime in model. You might also need to debug EF assemblies here's how to do it. Please let me know if you need more info or I have missed something.

推荐答案

我知道这可能不是您所期望的,但我想至少它会帮助您避免在这个方向上浪费更多时间.

I know this is probably not what you are expecting, but I guess at least it would help you not wasting more time in that direction.

好消息是问题不是由您的hackish"代码引起的.事实上,我能够在不使用该代码的情况下重现该问题.刚刚创建了一个包含 TestColQUOTE_HOUSE 表,将其导入到一个新的 edmx 上下文中,并刚刚从实体类中删除了生成的 TestCol 属性.然后我让该类继承了 DynamicEntity,使用默认构造函数创建了一个上下文,称为 context.QUOTE_HOUSE.ToList() 并且它爆炸了完全相同的异常.

The good news is that the problem is not caused by your "hackish" code. In fact I was able to reproduce the issue without using that code. Just created a QUOTE_HOUSE table containing a TestCol, imported it into a new edmx context and just deleted the generated TestCol property from the entity class. Then I've made the class inherit the DynamicEntity, created a context using the default constructor, called context.QUOTE_HOUSE.ToList() and it blew up with the exact same exception.

坏消息是您想要实现的目标是不可能的.EF 仅使用反射来映射对象空间"成员.它不提供任何类型扩展机制,例如 TypeDescriptor 或动态运行时(它甚至不允许您投影到动态对象).后者是可以理解的,因为每个动态对象可能具有不同的属性,并且不存在动态类型这样的东西.请注意,在运行时删除"列的技巧是有效的,因为它所做的与首先在代码中使用 NotMapped 基本相同,即该属性确实存在,但被 EF 忽略.

The bad news is that what you are trying to achieve is just not possible. EF uses nothing more and nothing less than reflection for mapping "object space" members. It does not offer any type extension mechanism like TypeDescriptor for instance, or dynamic runtime (it does not even allow you to project to dynamic objects). The later is understandable, since every dynamic object may have a different properties, and there is no such thing as dynamic type. Note that the trick with "removing" columns at run time works because what it does is basically the same as using NotMapped in code first, i.e. the property really exists, but is ignored by EF.

如果您对为什么实体在 CSpace 中而不在 OSpace 中感兴趣,答案包含在名为 OSpaceTypeFactory 的内部类中(在 System.Data.Entity.Core.Metadata.Edm 命名空间内) - .有一个方法叫做TryCreateStructuralType,它调用TryCreateMembers,如果返回false,则不添加类型.TryCreateMembers依次调用TryFindAndCreatePrimitiveProperties,传递反射提取的PropertyInfo列表,如果不能,则返回falseany CSpace 成员映射到OSpace 对象属性,从而有效防止类型被添加到OSpace 类型收藏.

If you are interested in why the entity is in CSpace but not in OSpace, the answer is contained in an internal class called OSpaceTypeFactory (inside System.Data.Entity.Core.Metadata.Edm namespace) - source. There is a method called TryCreateStructuralType, which calls TryCreateMembers and if it returns false, the type is not added. TryCreateMembers in turns calls TryFindAndCreatePrimitiveProperties, passing the PropertyInfo list extracted using reflection, and the later returns false if it cannot map any CSpace member to the OSpace object property, thus effectively preventing the type to be added to the OSpace type collection.

希望至少能满足您的好奇心:) 但同样,不幸的是,在运行时向 EF 实体添加"属性是一个死主意.

Hope at least that satisfied your curiosity :) But again, "adding" properties at runtime to EF entity is unfortunately a dead idea.

这篇关于实体框架实体不在 DataSpace.OSpace (_workspace.GetItemCollection(DataSpace.OSpace)) 中,但在 DataSpace.CSpace 中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆