2010-04-18 86 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); 
}