2016-06-23 42 views
15

Zostałem mucking wokół z XML s dla jednostki Framework. Starałem się stworzyć rodzaj podmiotu, który mógłby właściwości wstrzykiwany przy starcie, Najpierw stworzyłem DynamicEntity obiekt, który jest dynamicznyJednostka Entity Framework nie znajduje się w DataSpace.OSpace (_workspace.GetItemCollection (DataSpace.OSpace)), ale znajduje się w 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; 
    } 
} 

następnie jednostka dziedziczy z tym

public partial class QUOTE_HOUSE : DynamicEntity

(i robi wydaje się działać, gdy ręcznie ustawiam właściwości po uzyskaniu danych z db).

so based on this mechanism of removing properties Starałem się zrobić jeszcze jeden, który wstawia do właściwości plików XML, a cała sprawa wydaje się pomieścić ok (przynajmniej nie wysadzić na mapowanie, które zwykle robi, kiedy nie są w porządku plików XML var mappingCollection = new StorageMappingItemCollection(conceptualCollection, storageCollection, new[] {mappingXml.CreateReader()});).

Problem jest EF podczas wykonywania kwerendy wysadza z

Jednostka typu QUOTE_HOUSE nie jest częścią modelu dla bieżącego kontekstu.

Opis: Nieobsługiwany wyjątek wystąpił podczas wykonywania bieżącego żądania WWW. Zapoznaj się ze śledzeniem stosu, aby uzyskać więcej informacji o błędzie i miejscu jego powstania w kodzie.

Szczegóły wyjątku: System.InvalidOperationException: Typ jednostki QUOTE_HOUSE nie jest częścią modelu dla bieżącego kontekstu.

[InvalidOperationException. Typu jednostka QUOTE_HOUSE nie jest częścią modelu dla obecnym kontekście]
System.Data.Entity.Internal.InternalContext.UpdateEntitySetMappingsForType (typ entityType) +208
System.Data. Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType (typ entityType) +50

Który ja sięgają TryUpdateEntitySetMappingsForType w System.Data.Entity.Internal.InternalContext po załadowaniu WPB dla EF

Zasadniczo moje QUOTE_HOUSE nie jest w this._workspace.GetItemCollection(DataSpace.OSpace), gdzie UpdateEntitySetMappings próbuje je zamapować.

Sprawdza, czy to w this._entitySetMappingsCache.ContainsKey(entityType)) a ponieważ nie jest ona następnie próbuje aktualizacji mapowania iteracji nad this._workspace.GetItemCollection(DataSpace.OSpace) gdzie moja pozycja nie istnieje

Jednak widzę, że moja jednostka nie istnieje w this._workspace.GetItems<EntityContainer>(DataSpace.CSpace).

Pełne UpdateEntitySetMappings wygląda następujące:

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

Jak dostać się this._workspace.GetItemCollection podmioty (DataSpace.OSpace)? Dlaczego jednostka byłaby w CSpace, ale nie w OSpace?

EDIT: Dla tych, którzy może chcą się pęknięcia na laski, poniżej komponenty mogą być potrzebne do konfiguracji środowiska do odtworzenia problemu.

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 wygenerowane z db pierwszy, (kod DynamicEntity jest powyżej)

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

DbContext do bazy danych wymaga konstruktor przeciąża

public partial class EcomEntities : DbContext 
{ 

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

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

mechanizm, który robi zastrzyk kolumny (to jest szorstki prototyp tak bądź wyrozumiały, jak źle wygląda atm), po wstrzyknięciu spróbuj kolumny z napisem Wiem, że mapuje ona 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); 
    } 
} 

Inicjalizacja:

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

Powinieneś być w stanie wygenerować bazę danych Oracle od podmiotu QUOTE_HOUSE i wprowadzić pewne wartości obojętne, nie sądzę, trzeba się widok jak to wysadza na .ToList(). Po wygenerowaniu bazy danych dodaj dodatkową kolumnę do bazy danych, ale nie model (alter table QUOTE_HOUSE add TESTCOL Varchar2(20)) - aby kolumna w bazie danych była wstrzykiwana w czasie wykonywania w modelu. Może być również konieczne debugowanie EF assemblies here's how to do it. Daj mi znać, jeśli potrzebujesz więcej informacji lub coś przegapiłem.

+0

Jeśli ktoś potrzebuje dodatkowych szczegółów lub pomaga w konfigurowaniu środowiska, utworzyłem ten http://chat.stackexchange.com/rooms/41755/ef-hacking –

Odpowiedz

9

Wiem, że to prawdopodobnie nie jest to, czego oczekujesz, ale myślę, że przynajmniej pomogłoby ci nie marnować więcej czasu w tym kierunku.

Dobrą wiadomością jest to, że problem nie wynika z twojego "hackowskiego" kodu. W rzeczywistości udało mi się odtworzyć problem bez użycia tego kodu. Właśnie utworzono tabelę QUOTE_HOUSE zawierającą TestCol, zaimportowano ją do nowego kontekstu edmx i właśnie usunięto wygenerowaną właściwość TestCol z klasy encji. Następnie sprawiłem, że klasa odziedziczyła DynamicEntity, utworzyła kontekst przy użyciu domyślnego konstruktora o nazwie context.QUOTE_HOUSE.ToList() i wybuchł z tym samym wyjątkiem.

Zła wiadomość jest taka, że ​​to, co próbujesz osiągnąć, nie jest możliwe. EF nie używa niczego więcej i nic poza refleksją do mapowania elementów "przestrzeni obiektu". Nie oferuje on żadnego mechanizmu rozszerzenia typów, na przykład TypeDescriptor lub dynamicznego środowiska wykonawczego (nie pozwala nawet na wyświetlanie obiektów dynamicznych). Później jest zrozumiałe, ponieważ każdy dynamiczny obiekt może mieć inne właściwości i nie ma czegoś takiego jak dynamiczny typ. Zauważ, że sztuczka z "usuwaniem" kolumn w czasie wykonywania działa, ponieważ to, co robi, jest w zasadzie takie samo jak użycie NotMapped w kodzie pierwszym, tj. Właściwość istnieje, ale jest ignorowana przez EF.

Jeśli jesteś zainteresowany dlaczego jednostka jest w CSpace ale nie w OSpace, odpowiedź jest zawarta w klasie wewnętrznym zwanym OSpaceTypeFactory (wewnątrz System.Data.Entity.Core.Metadata.Edm namespace) - source. Istnieje metoda o nazwie TryCreateStructuralType, która wywołuje TryCreateMembers i jeśli zwraca false, typ nie zostanie dodany.TryCreateMembers z kolei wywołuje TryFindAndCreatePrimitiveProperties, przekazując listę PropertyInfo wyodrębnione przy użyciu odbicia, a później wraca false jeśli nie może odwzorowywać dowolnyCSpace członkiem obiekcie OSpace obiektu, a tym samym skutecznie zapobiegając typu mają być dodane do kolekcji OSpace typu.

Nadzieję, że przynajmniej to zaspokoiło twoją ciekawość :) Ale znowu, "dodawanie" właściwości w czasie rzeczywistym do encji EF jest niestety martwym pomysłem.