Mat*_*ius 15 c# workspace entity-framework database-metadata .net-4.5
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;
}
}
Run Code Online (Sandbox Code Playgroud)
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
基本上会发生什么我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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
实体如何进入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; }
}
Run Code Online (Sandbox Code Playgroud)
首先从db生成的实体,(DynamicEntity代码在上面)
public partial class QUOTE_HOUSE : DynamicEntity
{
public long UNIQUE_ID { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
数据库的DbContext需要构造函数重载
public partial class EcomEntities : DbContext
{
public EcomEntities(DbConnection connectionString)
: base(connectionString, false)
{
}
public virtual DbSet<QUOTE_HOUSE > QUOTE_HOUSE { get; set; }
....
}
Run Code Online (Sandbox Code Playgroud)
进行色谱柱注入的机制(它是一个粗略的原型因此对它看起来有多糟糕),当注入try string column时我知道它映射好了.
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);
}
}
Run Code Online (Sandbox Code Playgroud)
初始化:
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());
}
Run Code Online (Sandbox Code Playgroud)
你应该能够从实体生成oracle数据库QUOTE_HOUSE并输入一些虚拟值,不要认为你需要一个视图,因为它爆炸了.ToList().在生成数据库之后,将额外的列添加到数据库但不添加model(alter table QUOTE_HOUSE
add TESTCOL Varchar2(20)) - 以使数据库中的列在运行时在模型中注入.您可能还需要在此处调试EF程序集的方法.如果您需要更多信息或我错过了什么,请告诉我.
我知道这可能不是你所期待的,但我想至少它可以帮助你不浪费更多时间在那个方向.
好消息是问题不是由你的"hackish"代码引起的.事实上,我能够在不使用该代码的情况下重现该问题.刚刚创建了一个QUOTE_HOUSE包含a 的表TestCol,将其导入到新的edmx上下文中,只是TestCol从实体类中删除了生成的属性.然后我让类继承了DynamicEntity,使用默认构造函数创建了一个上下文,调用context.QUOTE_HOUSE.ToList()它并且它用完全相同的异常爆炸了.
坏消息是你想要实现的目标是不可能的.EF仅使用反射来映射"对象空间"成员.它不提供任何类型的扩展机制TypeDescriptor,例如动态运行时(它甚至不允许您投影到动态对象).后者是可以理解的,因为每个动态对象可能具有不同的属性,并且没有动态类型这样的东西.请注意,在运行时"删除"列的技巧可行,因为它的作用基本上与NotMapped在代码中首先使用相同,即属性确实存在,但被EF忽略.
如果您对实体所在CSpace但不在其中的原因感兴趣OSpace,则答案包含在名为OSpaceTypeFactory(内部System.Data.Entity.Core.Metadata.Edm命名空间)的内部类中- 源.有一个调用的方法TryCreateStructuralType,调用TryCreateMembers它false,如果返回,则不添加类型.TryCreateMembers轮流调用TryFindAndCreatePrimitiveProperties,传递PropertyInfo使用反射提取的列表,false如果它不能将任何 CSpace成员映射到OSpace对象属性,则后者返回,从而有效地防止将类型添加到OSpace类型集合中.
希望至少能满足你的好奇心:)但是,再次,在运行时向EF实体"添加"属性是不幸的想法.