2010-04-18 20 views

Odpowiedz

11

udało mi się rozwiązać ten problem przy użyciu biblioteki genialny, położony w CodePlex (dzięki uprzejmości Brandon Haynes), o nazwie "Entity Framework Runtime model Adapter", dostępny tutaj: http://efmodeladapter.codeplex.com/

mam manipulowane to trochę, aby dopasować się do naszych potrzeb i bez potrzeby zastępowania w ogóle kodu projektanta.

Tak, jestem dobry.

Dzięki i tak, a szczególnie Brandon, niesamowita robota!

4

Potrzebuję danych importu z bazy danych PostgreSQL. Domyślnie używa schematu "public". Używam Entity Framework CTP 4 "Code first". Domyślnie używa schematu "dbo". Aby go zmienić w czasie wykonywania, używam:

public class PublicSchemaContext : DbContext 
{ 
    protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder builder) 
    { 
     builder.Entity<series_categories>().MapSingleType().ToTable("[public].[series_categories]"); 
    } 

    public DbSet<series_categories> series_categories { get; set; } 
} 

Działa w celu wybrania, wstawienia, aktualizacji i usunięcia danych. Tak więc następny test:

[Test] 
     public void AccessToPublicSchema() 
     { 
      // Select 
      var db = new PublicSchemaContext(); 
      var list = db.series_categories.ToList(); 
      Assert.Greater(list.Count, 0); 
      Assert.IsNotNull(list.First().series_category); 

      // Delete 
      foreach (var item in db.series_categories.Where(c => c.series_category == "Test")) 
       db.series_categories.Remove(item); 
      db.SaveChanges(); 

      // Insert 
      db.series_categories.Add(new series_categories { series_category = "Test", series_metacategory_id = 1 }); 
      db.SaveChanges(); 

      // Update 
      var test = db.series_categories.Single(c => c.series_category == "Test"); 
      test.series_category = "Test2"; 
      db.SaveChanges(); 

      // Delete 
      foreach (var item in db.series_categories.Where(c => c.series_category == "Test2")) 
       db.series_categories.Remove(item); 
      db.SaveChanges(); 
     } 
+0

To rozwiązanie działało idealnie dla mnie. W moim przypadku wszystko, co musiałem zrobić, to przełączać się między przedrostkami tabeli. Jednak składnia była dla mnie nieco inna: modelBuilder.Entity () .Map (x => x.ToTable (_tablePrefix + tableName)); Dzięki Serg! – adamisnt

+0

Miło to słyszeć! Składnia będzie się zmieniać, ponieważ mój kod został napisany i przetestowany na Entity Framework CTP4. – msa7

24

Cóż, szukałem tego kawałka kodu w Internecie. W końcu musiałem to zrobić sam. Jest oparty na adapterze Brandona Haynesa, ale ta funkcja jest wszystkim, czego potrzebujesz, aby zmienić schemat w środowisku wykonawczym - i nie musisz zastępować generowanych automatycznie konstruktorów kontekstów.

public static EntityConnection Create(
    string schema, string connString, string model) 
{ 
    XmlReader[] conceptualReader = new XmlReader[] 
    { 
     XmlReader.Create(
      Assembly 
       .GetExecutingAssembly() 
       .GetManifestResourceStream(model + ".csdl") 
     ) 
    }; 
    XmlReader[] mappingReader = new XmlReader[] 
    { 
     XmlReader.Create(
      Assembly 
       .GetExecutingAssembly() 
       .GetManifestResourceStream(model + ".msl") 
     ) 
    }; 

    var storageReader = XmlReader.Create(
     Assembly 
      .GetExecutingAssembly() 
      .GetManifestResourceStream(model + ".ssdl") 
    ); 
    XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/02/edm/ssdl"; 

    var storageXml = XElement.Load(storageReader); 

    foreach (var entitySet in storageXml.Descendants(storageNS + "EntitySet")) 
    { 
     var schemaAttribute = entitySet.Attributes("Schema").FirstOrDefault(); 
     if (schemaAttribute != null) 
     { 
      schemaAttribute.SetValue(schema); 
     } 
    } 
    storageXml.CreateReader(); 

    StoreItemCollection storageCollection = 
     new StoreItemCollection(
      new XmlReader[] { storageXml.CreateReader() } 
     ); 
    EdmItemCollection conceptualCollection = new EdmItemCollection(conceptualReader); 
    StorageMappingItemCollection mappingCollection = 
     new StorageMappingItemCollection(
      conceptualCollection, storageCollection, mappingReader 
     ); 

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

Wynikowy EntityConnection powinien zostać przekazany jako parametr podczas tworzenia kontekstu. Możesz go zmodyfikować, aby wszystkie modele ssdl zostały zmodyfikowane przez tę funkcję, a nie tylko podaną.

+0

Awesome! To rozwiązanie mi pomogło. Pracuję z bazą danych DB2, która najwyraźniej uważa, że ​​schematy są bardziej na poziomie baz danych SQLServer. Praca z EF5. –

+1

Zauważyłem, że tworzenie wystąpień tych trzech kolekcji jest bardzo kosztowne (czas), więc przechowuję zmodyfikowane Worspaces w słowniku, więc nie muszę go modyfikować dla każdej instancji Context. –

+0

Zawijam już tworzenie EntityConnection, więc potrzebowałem tylko dodać część, która nadpisuje schemat. Szkoda, że ​​podobna funkcja nie jest obsługiwana po wyjęciu z pudełka. Dzięki. – JCallico

2

Nie jako odpowiedź jako taką, ale jako kontynuację metody Utwórz [EntityConnection] Jana Matouseka pokazującej, jak korzystać z DbContext. Uwaga: DB jest typem DbContext przekazywanym do ogólnego repozytorium.

public TxRepository(bool pUseTracking, string pServer, string pDatabase, string pSchema="dbo") 
{ 
    // make our own EF database connection string using server and database names 
    string lConnectionString = BuildEFConnectionString(pServer, pDatabase); 

    // do nothing special for dbo as that is the default 
    if (pSchema == "dbo") 
    { 
     // supply dbcontext with our connection string 
     mDbContext = Activator.CreateInstance(typeof(DB), lConnectionString) as DB; 
    } 
    else // change the schema in the edmx file before we use it! 
    { 
     // Create an EntityConnection and use that to create an ObjectContext, 
     // then that to create a DbContext with a different default schema from that specified for the edmx file. 
     // This allows us to have parallel tables in the database that we can make available using either schema or synonym renames. 
     var lEntityConnection = CreateEntityConnection(pSchema, lConnectionString, "TxData"); 

     // create regular ObjectContext 
     ObjectContext lObjectContext = new ObjectContext(lEntityConnection); 

     // create a DbContext from an existing ObjectContext 
     mDbContext = Activator.CreateInstance(typeof(DB), lObjectContext, true) as DB; 
    } 

    // finish EF setup 
    SetupAndOpen(pUseTracking); 
} 
0

byłem w stanie konwertować roztwór z Janem Matousek pracować w VB.NET 2013 z Entity Framework 6. postaram się wyjaśnić jak użyć kodu w VB.NET.

Mamy bazę danych JD Edwards, która wykorzystuje różne schematy dla każdego środowiska (TESTDTA, CRPDTA, PRODDTA). Dzięki temu przełączanie między środowiskami jest uciążliwe, ponieważ musisz ręcznie zmodyfikować plik .edmx, jeśli chcesz zmienić środowisko.

Pierwszym krokiem jest utworzenie częściowej klasy, która pozwala przekazać wartość do konstruktora swoich jednostek, domyślnie używa wartości z pliku konfiguracyjnego.

Partial Public Class JDE_Entities 
    Public Sub New(ByVal myObjectContext As ObjectContext) 
     MyBase.New(myObjectContext, True) 
    End Sub 
End Class 

Następnie utwórz funkcję, która zmodyfikuje plik schematu sklepu .ssdl w pamięci.

Public Function CreateObjectContext(ByVal schema As String, ByVal connString As String, ByVal model As String) As ObjectContext 

    Dim myEntityConnection As EntityConnection = Nothing 

    Try 

     Dim conceptualReader As XmlReader = XmlReader.Create(Me.GetType().Assembly.GetManifestResourceStream(model + ".csdl")) 
     Dim mappingReader As XmlReader = XmlReader.Create(Me.GetType().Assembly.GetManifestResourceStream(model + ".msl")) 
     Dim storageReader As XmlReader = XmlReader.Create(Me.GetType().Assembly.GetManifestResourceStream(model + ".ssdl")) 

     Dim storageNS As XNamespace = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl" 

     Dim storageXml = XDocument.Load(storageReader) 
     Dim conceptualXml = XDocument.Load(conceptualReader) 
     Dim mappingXml = XDocument.Load(mappingReader) 

     For Each myItem As XElement In storageXml.Descendants(storageNS + "EntitySet") 
      Dim schemaAttribute = myItem.Attributes("Schema").FirstOrDefault 

      If schemaAttribute IsNot Nothing Then 
       schemaAttribute.SetValue(schema) 
      End If 

     Next 

     storageXml.Save("storage.ssdl") 
     conceptualXml.Save("storage.csdl") 
     mappingXml.Save("storage.msl") 

     Dim storageCollection As StoreItemCollection = New StoreItemCollection("storage.ssdl") 
     Dim conceptualCollection As EdmItemCollection = New EdmItemCollection("storage.csdl") 

     Dim mappingCollection As StorageMappingItemCollection = New StorageMappingItemCollection(conceptualCollection, storageCollection, "storage.msl") 


     Dim workspace = New MetadataWorkspace() 
     workspace.RegisterItemCollection(conceptualCollection) 
     workspace.RegisterItemCollection(storageCollection) 
     workspace.RegisterItemCollection(mappingCollection) 

     Dim connectionData = New EntityConnectionStringBuilder(connString) 
     Dim connection = DbProviderFactories.GetFactory(connectionData.Provider).CreateConnection() 

     connection.ConnectionString = connectionData.ProviderConnectionString 

     myEntityConnection = New EntityConnection(workspace, connection) 

     Return New ObjectContext(myEntityConnection) 

    Catch ex As Exception 

    End Try 

End Function 

Upewnij się, że wartość ustalony storageNS nazw pasuje do jednego używanego w kodzie, można zobaczyć to przez debugowanie kodu i sprawdzenie zmiennej storageXML zobaczyć, co się rzeczywiście używany.

Teraz możesz przekazać nową nazwę schematu i inne informacje o połączeniu bazy danych w czasie wykonywania podczas tworzenia jednostek. Koniec z ręcznymi zmianami .edmx.

Using Context As New JDE_Entities(CreateObjectContext("NewSchemaNameHere", ConnectionString_EntityFramework("ServerName", "DatabaseName", "UserName", "Password"), "JDE_Model")) 

      Dim myWO = From a In Context.F4801 Where a.WADOCO = 400100 

      If myWO IsNot Nothing Then 
       For Each r In myWO 
        Me.Label1.Text = r.WADL01 
       Next 
      End If 
     End Using 

Były .net bibliotek używanych:

Imports System.Data.Entity.Core.EntityClient 
Imports System.Xml 
Imports System.Data.Common 
Imports System.Data.Entity.Core.Metadata.Edm 
Imports System.Reflection 
Imports System.Data.Entity.Core.Mapping 
Imports System.Data.Entity.Core.Objects 
Imports System.Data.Linq 
Imports System.Xml.Linq 

nadzieję, że pomoże ktoś tam z tymi samymi problemami.

0

Miałem wiele problemów z uruchomieniem tego, gdy korzystałem z EF6 z ​​usługą danych OData, więc musiałem znaleźć alternatywne rozwiązanie. W moim przypadku tak naprawdę nie musiałem robić tego w locie. Mógłbym uniknąć zmiany schematu podczas wdrażania w niektórych środowiskach testowych iw instalatorze.

Użyj Mono.Cecil, aby przepisać osadzone zasoby .ssdl prosto w bibliotekach DLL. Działa to dobrze w moim przypadku.

Oto uproszczony przykład tego, jak można to zrobić:

var filename = "/path/to/some.dll" 
var replacement = "Schema=\"new_schema\""; 

var module = ModuleDefinition.ReadModule(filename); 
var ssdlResources = module.Resources.Where(x => x.Name.EndsWith(".ssdl")); 

foreach (var resource in ssdlResources) 
{ 
    var item = (EmbeddedResource)resource; 
    string rewritten; 

    using (var reader = new StreamReader(item.GetResourceStream())) 
    { 
     var text = reader.ReadToEnd(); 
     rewritten = Regex.Replace(text, "Schema=\"old_schema\"", replacement); 
    } 

    var bytes = Encoding.UTF8.GetBytes(rewritten); 
    var newResource = new EmbeddedResource(item.Name, item.Attributes, bytes); 

    module.Resources.Remove(item); 
    module.Resources.Add(newResource); 
} 
Powiązane problemy