我一直在用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
(当我从数据库获取数据后手动设置属性时,它似乎确实有效)。
因此,基于这种删除属性的机制,我尝试做另一个将属性插入 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
TryUpdateEntitySetMappingsForType
我在System.Data.Entity.Internal.InternalContext
加载 pdb 后追踪到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);
}
}
}
实体如何进入 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; }
}
首先从 db 生成的实体,(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; }
....
}
进行列注入的机制(它是一个粗略的原型,所以请原谅它看起来有多糟糕),当注入尝试字符串列时,我知道它映射正常。
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());
}
您应该能够从实体生成 oracle 数据库QUOTE_HOUSE
并输入一些虚拟值,不要认为您需要视图,因为它会爆炸.ToList()
。生成数据库后,将附加列添加到数据库而不是模型(alter table QUOTE_HOUSE
add TESTCOL Varchar2(20)
) - 使数据库中的列在运行时注入模型中。您可能还需要在此处调试 EF 程序集,这是如何进行的。如果您需要更多信息或我遗漏了什么,请告诉我。