2016-06-23 1 views
15

엔티티 프레임 워크에 대해 XML을 사용하고 있습니다. I는 속성 런타임 주입 수도 엔티티의 타입을 만들려고 먼저 I는엔티티 프레임 워크 엔티티가 DataSpace.OSpace (_workspace.GetItemCollection (DataSpace.OSpace))에 없지만 DataSpace.CSpace에 있습니다.

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

상속 동적 DynamicEntity 객체를 생성 (그것을 수행 db에서 데이터를 얻은 후 속성을 수동으로 설정할 때 작동하는 것 같습니다.

so based on this mechanism of removing properties XML에 속성을 삽입하는 또 다른 작업을 시도했는데 모든 것이 정상적으로 유지되는 것처럼 보입니다. 적어도 XML이 올바르지 않을 때 일반적으로 수행하는 매핑에는 불필요합니다 (var mappingCollection = new StorageMappingItemCollection(conceptualCollection, storageCollection, new[] {mappingXml.CreateReader()});). 쿼리를 실행함으로써

엔티티 타입 QUOTE_HOUSE 현재 컨텍스트 모델의 일부가 아닌 경우와 불면

문제 EF이다.

설명 현재 웹 요청 을 실행하는 중 처리되지 않은 예외가 발생했습니다. 오류에 대한 정보와 코드에서 오류가 발생한 위치에 대한 정보는 에 대한 스택 추적을 검토하십시오.

예외 정보 : System.InvalidOperationException : 엔터티 유형 QUOTE_HOUSE가 현재 컨텍스트의 모델에 속해 있지 않습니다.

[InvalidOperationException이 :. 엔티티 타입 QUOTE_HOUSE 가 현재 컨텍스트 모델의 일부가 아니다]
System.Data.Entity.Internal.InternalContext.UpdateEntitySetMappingsForType (유형 entityType) 208
System.Data. Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType 내가 EF

에 대한로드 PDB 후 System.Data.Entity.Internal.InternalContextTryUpdateEntitySetMappingsForType 추적 (유형 entityType) +50

기본적으로 내 QUOTE_HOUSEthis._workspace.GetItemCollection(DataSpace.OSpace)이 아니며 여기서 UpdateEntitySetMappings은 매핑을 시도합니다. 이 this._entitySetMappingsCache.ContainsKey(entityType))에 있다면

그것은 확인하고 그렇지 않은 이후로는 내 엔티티가 this._workspace.GetItems<EntityContainer>(DataSpace.CSpace)에 존재하는 것을 볼 수있다 그러나 내 항목이

존재하지 않는 경우 this._workspace.GetItemCollection(DataSpace.OSpace) 반복 갱신 매핑을 시도합니다.

전체 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에없는 이유는 무엇입니까?

편집 : 현상금에 균열을 가지고 싶어 수있는 사람들을 위해 은, 아래가 설정하는 환경 문제를 재현해야 할 수 있습니다 구성 요소입니다. 엔티티가 DB 제에서 발생

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

, 데이터베이스

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

메커니즘 과부하 요구 (DynamicEntity 코드 위이다) 그것이 atm을 보는지 얼마나 나쁜지에 대해 용서하십시오.) 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); 
    } 
} 

초기화 :

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에서 오라클 데이터베이스를 생성하고 몇 가지 더미 값을 입력 할 수 있어야한다, 그것은 .ToList()에 불면 당신이 볼 필요가 있다고 생각하지 않습니다. 데이터베이스를 생성 한 후 데이터베이스에 열을 추가하지만 모델 (alter table QUOTE_HOUSE add TESTCOL Varchar2(20))은 추가하지 않습니다. 런타임에 모델에 삽입되는 데이터베이스의 열을 갖습니다. EF assemblies here's how to do it을 디버그해야 할 수도 있습니다. 더 많은 정보가 필요하거나 뭔가를 놓친 경우 알려주십시오.

+0

추가 정보가 필요하거나 환경 설정에 도움이 필요한 사람은 http://chat.stackexchange.com/rooms/41755/ef-hacking을 만들었습니다. –

답변

9

나는 이것이 아마도 당신이 기대하는 바가 아님을 안다. 그러나 나는 적어도 그것이 그 방향으로 더 많은 시간을 낭비하지 않도록 도와 줄 것이라고 생각한다.

좋은 소식은 문제가 "hackish"코드 때문이 아니라는 것입니다. 사실 나는 그 코드를 사용하지 않고 문제를 재현 할 수있었습니다. TestCol을 포함하는 QUOTE_HOUSE 테이블을 만든 다음 새 edmx 컨텍스트로 가져 와서 생성 된 TestCol 속성을 엔티티 클래스에서 삭제했습니다. 그런 다음 클래스가 DynamicEntity을 상속 받도록하고 context.QUOTE_HOUSE.ToList()이라는 기본 생성자를 사용하여 컨텍스트를 만들었으며 완전히 동일한 예외가 발생했습니다.

나쁜 소식은 당신이 성취하려는 것은 단지 불가능하다는 것입니다. EF는 "객체 공간"멤버를 매핑하는 데 반사 효과를 사용합니다. 예를 들어 TypeDescriptor과 같은 유형 확장 메커니즘이나 동적 런타임은 제공하지 않습니다 (동적 객체에 투영 할 수는 없습니다). 모든 동적 객체는 서로 다른 속성을 가질 수 있으며 동적 인 유형과 같은 것이 없으므로 나중에 이해할 수 있습니다. 런타임에 열을 제거하는 트릭은 코드에서 처음으로 NotMapped을 사용하는 것과 기본적으로 동일하므로 속성이 실제로 존재하지만 EF에서는 무시됩니다. source - 당신이 엔티티가 CSpace에 있지만 OSpace에, 대답이 OSpaceTypeFactory (네임 스페이스 System.Data.Entity.Core.Metadata.Edm 내부)라는 내부 클래스에 포함되지 이유에 관심이 있다면

. TryCreateStructuralType이라는 메서드가 있는데이 메서드는 TryCreateMembers을 호출하고 false을 반환하면 형식이 추가되지 않습니다.회전에 TryCreateMembers 반사를 이용하여 추출한 PropertyInfo 목록을 전달 TryFindAndCreatePrimitiveProperties 호출하고, 이후 리턴 false 따라서 그것은 효과적으로 OSpace 형 컬렉션에 추가 할 수있는 입력을 방지 OSpace 객체 속성 어떤CSpace 부재 을 매핑 할 수없는 경우.

적어도 당신의 호기심을 만족 시키길 바랍니다. :) 그러나 다시 한번 EF 엔티티에 런타임에 속성을 추가하는 것은 불행히도 죽은 아이디어입니다.

관련 문제