实体框架实体不在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

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

问题描述

我已经在实体框架中使用 XML 。我试图创建一种可以在运行时注入属性的实体,
首先我创建了动态


$ DynamicEntity b $ b

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

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

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

result =;
返回true;
}
}

然后实体继承自



public partial class QUOTE_HOUSE:DynamicEntity



(它似乎工作当我从数据库获取数据后手动设置属性)。



所以基于这种删除属性的机制我试图做另一个将属性插入到XML中的东西,整个事情似乎都保持不变(至少它不会对映射产生任何影响它通常在XML不正确时执行 var mappingCollection = new StorageMappingItemCollection(conceptsCollection,storageCollection,new [] {m​​appingXml.CreateReader()}); )。



执行查询时遇到的问题是使用


实体类型为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


System.Data.Entity.Internal.InternalContext 之后跟踪到 TryUpdateEntitySetMappingsForType 加载pdb for EF



基本上我的 QUOTE_HOUSE 不在 this._workspace.GetItemCollection(DataSpace.OSpace)其中 UpdateEntitySetMappings 尝试映射它。 >

它检查是否在 this._entitySetMap pingsCache.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);
堆栈< EntityType> entityTypeStack = new Stack< EntityType>();
foreach(项目中的EntityType entityType1)
{
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))
{
列表< 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);
类型clrType1 = objectItemCollection.GetClrType((StructuralType)entityType1);
类型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;
数据库=数据库;
DatabaseType = databaseType;
ConnectionString = connectionString;
EntityType = enitityType;
}

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

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

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

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

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

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

public Type Type {get;组; }
}

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

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

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

  public partial class EcomEntities:DbContext 
{

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

public virtual DbSet"E_HOUSE> QUOTE_HOUSE {get;组; }
....
}

执行列注入的机制(它是一个粗糙的原型,所以要宽恕它看起来有多糟糕),当注意尝试字符串列我知道它映射好。

  public static class EntityConnectionExtensions 
{
public static IEnumerable< XElement> ElementAnyNS< T>(此IEnumerable< T>源,字符串localName)
其中T:XContainer
{
返回source.Elements()。其中​​(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();

如果(matchingRemoveSelectParts.Any())
{
foreach(匹配RemoveSelectParts中的var matchingRemoveSelectPart)
{
var definitionsQuery = element.ElementsAnyNS(DefiningQuery )。单();
definitionsQuery.Value = defineQuery.Value.Replace(string.Format(,\\\
\{0} \.\{1} \AS \{1} \\,matchingRemoveSelectPart.Table,matchingRemoveSelectPart.Column),);
}
}
else
{
var nodesToRemove = element.Nodes()
.Where(o =>
o是XElement
&((XElement)o).Attribute(Name)!= null
&& tableAndColumn.Any(oo => oo.IsRemove&&((XElement) o).Attribute(Name)。Value == oo.Column));

foreach(nodeToRemove.ToList()中的var node)
{
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)。
if(new [] {Type = \number\,Type = \varchar2\,Type = \date\ => element.ToString()。Contains(o)))
{
foreach(elementToAdd中的var columnToModify)
{
var columnToAdd =(ColumnToAdd)columnToModify;

var type = new [] {typeof(decimal),typeof(float),typeof(int),typeof(bool)}包含(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)}。包含(columnToAdd.Type)? 2:0;
}

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


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

如果(!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 \}任何(
o => element.ToString()。Contains(o)))
{
foreach(elementToAdd中的var columnToModify)
{
var columnToAdd =(ColumnToAdd)columnToModify;

var type = new [] {typeof(decimal),typeof(float),typeof(int),typeof(bool)}包含(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)}。包含(columnToAdd.Type)? 2:0;
}

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


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

如果(!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&&&b $ b 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)。
foreach(在matchAddSelectParts中的var matchingAddSelectPart)
{
var defineQuery = element.ElementsAnyNS(DefiningQuery)。
var schemaRegex = new Regex(string.Format(\\\\FRM \\\([a-zA-Z0-9] *)\\\.\\\ \\\{0} \\\,matchingAddSelectPart.Table));
var schema = schemaRegex.Matches(definitionsQuery.Value)[0] .Groups [1] .Value;
definitionsQuery.Value = defineQuery.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)。$)
{
var matchingAddSelectParts = tableAndColumn.Where(o =>!o.IsRemove&& o.Table = = element.Attribute(StoreEntitySet)。Value);
foreach(match matchingAddSelectParts中的var matchingAddSelectPart)
{
element.Add(new XElement(element.GetDefaultNamespace()+ScalarProperty,新的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!=))。选择(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(entitySet.Elements()中的var entityContainerEntitySet)
{
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 conceptsCollection = new EdmItemCollection(new [] {conceptualXml.CreateReader()});
var mappingCollection = new StorageMappingItemCollection(conceptsCollection,storageCollection,new [] {m​​appingXml.CreateReader()});

var workspace = new MetadataWorkspace();

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

返回新的EntityConnection(工作区,连接);
}
}

初始化:

  public ActionResult QUOTE_HOUSE()
{
var onlineDocs = Enumerable.Empty"E_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);
使用(var db = new EcomEntities(entityConnection))
{
onlineDocs = db.QUOTE_HOUSE.Take(10);
}

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

您应该能够从实体生成oracle数据库 QUOTE_HOUSE 并输入一些虚拟值,不要以为您在 .ToList()之前需要查看。生成数据库之后,添加附加列到数据库而不是模型( alter table QUOTE_HOUSE
添加TESTCOL Varchar2(20)
) - 在数据库中有列在运行时注入模型。您可能还需要调试 EF程序集这里是如何做到的。请让我知道,如果你需要更多的信息,或者我错过了某些东西。

我知道这可能不是你所期待的,但是我猜想这至少会帮助你不要浪费更多的时间在这个方向。



好消息是,这个问题不是由你的黑客代码造成的。事实上,我没有使用该代码就能够重现这个问题。刚刚创建了一个包含 TestCol QUOTE_HOUSE 表,将其导入到新的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 ,传递提取的 PropertyInfo 列表使用反射,如果不能将任何 CSpace 成员映射到 c>,则返回 false 操作空间对象属性,从而有效地防止将类型添加到 OSpace 类型集合。希望至少满足您的好奇心:)但是,再次,运行时将属性添加到EF实体是不幸的一个死主意。


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

then entity inherits from this

public partial class QUOTE_HOUSE : DynamicEntity

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

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()});).

Problem is EF when executing query blows up with

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.

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

[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

Which I traced to TryUpdateEntitySetMappingsForType in System.Data.Entity.Internal.InternalContext after loading pdb for EF

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

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

However I can see that my entity does exist in this._workspace.GetItems<EntityContainer>(DataSpace.CSpace).

Full UpdateEntitySetMappings looks following:

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

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

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

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

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

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

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(", \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);
    }
}

Initialization:

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

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.

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.

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.

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.

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天全站免登陆