2010-04-18 88 views

回答

11

我已經成功使用了輝煌的圖書館,坐落在CodePlex上(布蘭登·海恩斯提供),命名爲「實體框架運行時間模型適配器」來解決這個問題,可在這裏: http://efmodeladapter.codeplex.com/

我已經調整了它有點符合我們的需求,並且根本不需要替換設計器代碼。

所以,我很好。

無論如何,特別是布蘭登,令人驚歎的工作!

4

我需要從postgres數據庫導入數據。它默認使用schema「public」。所以我使用Entity Framework CTP 4「Code first」。它默認使用模式「dbo」。要在運行時改變它,我使用:

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

它適用於選擇,插入,更新和刪除數據。所以下一個測試通過:

[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

該解決方案完全爲我工作。就我而言,我需要做的就是切換表前綴。但是,我的語法略有不同:modelBuilder.Entity ().Map(x => x.ToTable(_tablePrefix + tableName)); 謝謝Serg! – adamisnt 2011-04-06 23:49:11

+0

很高興聽到!語法將會改變,因爲我的代碼是在Entity Framework CTP4上編寫和測試的。 – msa7 2011-04-20 14:26:27

24

嗯,我正在尋找這段代碼在互聯網周圍。最後我必須自己做。它基於Brandon Haynes適配器,但是這個功能是您在運行時更改模式所需的全部功能 - 而且您無需替換自動生成的上下文構造函數。

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

當實例化上下文時,產生的EntityConnection應作爲參數傳遞。你可以修改它,所以所有ssdl模型都被這個函數修改,不僅僅是指定的。

+0

太棒了!這個解決方案爲我做了竅門。我正在使用DB2數據庫,這顯然認爲模式更像SQLServer數據庫。使用EF5。 – 2013-08-13 23:08:30

+1

我注意到,創建這三個集合的實例非常昂貴(時間),所以我將修改的Worspaces存儲在一個Dictionary中,所以我不需要爲每個Context實例修改它。 – 2013-08-15 08:59:22

+0

我已經包裝了EntityConnection創建,所以我只需要添加覆蓋架構的部分。太糟糕了,一個類似的功能不支持開箱即用。謝謝。 – JCallico 2014-02-24 18:33:09

2

本身不是答案,而是Jan Matousek的Create [EntityConnection]方法中的後續操作,顯示瞭如何使用DbContext。注意DB是傳遞給通用存儲庫的DbContext類型。

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

我能夠將Jan Matousek的解決方案轉換爲vb.net 2013與實體框架6一起工作。我也將嘗試解釋如何在vb.net中使用代碼。

我們有一個JD Edwards數據庫,它爲每個環境使用不同的Schema(TESTDTA,CRPDTA,PRODDTA)。這使得在環境之間切換很麻煩,因爲如果要更改環境,必須手動修改.edmx文件。

第一步是創建一個部分類,允許您將值傳遞給實體的構造函數,默認情況下它使用配置文件中的值。

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

接下來創建將修改您的存儲架構.ssdl文件在內存中的函數。

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 

確保storageNS命名空間硬編碼值符合您的代碼中使用的,可以通過代碼調試,並且檢查storageXML變量來看看有什麼實際使用的查看此。

現在,您可以在創建實體時在運行時傳遞新的模式名稱和不同的數據庫連接信息。不需要更多的手動.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 

這些人使用的.NET庫:

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 

希望與同樣的問題,幫助那裏的人。

0

在使用EFata和OData數據服務時,我有很多問題需要解決,所以我不得不尋找替代解決方案。就我而言,我並不是真的需要這樣做。在部署到某些測試環境時以及在安裝程序中,我可以避免更改架構。

使用 Mono.Cecil直接在DLL中重寫嵌入的.ssdl資源。這在我的情況下工作得很好。

這裏是你如何能做到這一個簡單的例子:

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