2010-04-18 15 views

risposta

11

sono riuscito a risolvere questo problema utilizzando una libreria brillante, con sede a CodePlex (per gentile concessione di Brandon Haynes), il nome di "Entity Framework runtime Adapter Model", disponibile qui: http://efmodeladapter.codeplex.com/

ho ottimizzato un po ', per adattarsi ai nostri bisogni e senza la necessità di sostituire il codice del designer.

Quindi, sto bene.

Grazie comunque, e soprattutto a Brandon, lavoro incredibile!

4

Ho bisogno di importare dati dal database postgres. Di default usa lo schema "pubblico". Quindi io uso Entity Framework CTP 4 "Code first". Di default usa lo schema "dbo". Per cambiarlo in runtime uso:

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

Funziona per selezionare, inserire, aggiornare e cancellare i dati. Quindi il prossimo test di passaggio:

[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

Questa soluzione ha funzionato perfettamente per me. Nel mio caso, tutto quello che dovevo fare era passare attraverso i prefissi delle tabelle. Tuttavia, la sintassi per me era leggermente diversa: modelBuilder.Entity () .Map (x => x.ToTable (_tablePrefix + tableName)); Grazie Serg! – adamisnt

+0

Felice di sentire! La sintassi cambierà, poiché il mio codice è stato scritto e testato su Entity Framework CTP4. – msa7

24

Beh, stavo cercando questo pezzo di codice su Internet. Alla fine ho dovuto farlo da solo. È basato sull'adattatore Brandon Haynes, ma questa funzione è tutto ciò che serve per modificare lo schema in fase di esecuzione e non è necessario sostituire i costruttori di contesto generati automaticamente.

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

La EntityConnection risultante deve essere passata come parametro durante l'istanziazione del contesto. È possibile modificarlo, quindi tutti i modelli ssdl vengono modificati da questa funzione, non solo da quella specificata.

+0

Fantastico! Questa soluzione ha fatto il trucco per me. Sto lavorando con un database DB2, che a quanto pare considera gli schemi più sul livello dei database SQLServer. Lavorare con EF5. –

+1

Ho notato che creare istanze di queste tre raccolte è molto costoso (tempo), quindi memorizzo i Worspace modificati in un dizionario, quindi non ho bisogno di modificarlo per ogni istanza di Context. –

+0

Stavo già avvolgendo la creazione di EntityConnection, quindi ho solo bisogno di aggiungere la parte che sovrascrive lo schema. Peccato che una funzionalità simile non sia supportata immediatamente. Grazie. – JCallico

2

Non è una risposta di per sé ma un seguito sul metodo Crea [EntityConnection] di Jan Matousek che mostra come utilizzare da un DbContext. Nota DB è il tipo DbContext passato al repository generico.

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

sono stato in grado di convertire la soluzione da Jan Matousek a lavorare in vb.net 2013, con Entity Framework 6. Cercherò anche di spiegare come utilizzare il codice in vb.net.

Abbiamo un database JD Edwards che utilizza diversi schemi per ogni ambiente (TESTDTA, CRPDTA, PRODDTA). Ciò rende difficile il passaggio da un ambiente all'altro, in quanto è necessario modificare manualmente il file .edmx se si desidera modificare gli ambienti.

Il primo passaggio consiste nel creare una classe parziale che consente di passare un valore al costruttore delle entità, per impostazione predefinita utilizza i valori del file di configurazione.

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

Quindi creare la funzione che modificherà il file .ssdl dello schema del negozio in memoria.

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 

Assicurarsi che lo spazio dei nomi valore hardcoded storageNS corrisponde a quello utilizzato nel codice, è possibile visualizzare questo il debug del codice ed esaminando la variabile storageXML per vedere ciò che è stato effettivamente utilizzato.

Ora è possibile passare un nuovo nome schema e diverse informazioni di connessione al database in fase di runtime quando si creano le entità. Non sono necessarie ulteriori modifiche manuali .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 

Queste erano le librerie .NET utilizzati:

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 

Speranza che aiuta qualcuno là fuori con gli stessi problemi.

0

Ho avuto un sacco di problemi nel farlo funzionare quando si utilizza EF6 con un servizio dati OData, quindi ho dovuto trovare una soluzione alternativa. Nel mio caso, non avevo davvero bisogno di farlo al volo. Potrei farla franca cambiando lo schema durante la distribuzione in alcuni ambienti di test e nel programma di installazione.

Utilizzare Mono.Cecil per riscrivere le risorse incorporate .ssdl direttamente nelle DLL. Questo funziona bene nel mio caso.

Ecco un esempio semplificato di come si può fare questo:

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