実行時にエンティティのストレージスキーマを変更する必要があります。私はここで利用できる素晴らしい投稿をフォローしました: http://blogs.Microsoft.co.il/blogs/idof/archive/2008/08/22/change-entity-framework-storage-db-schema -in-runtime.aspx?CommentPosted = true#commentmessage
これは完全に機能しますが、クエリに対してのみ機能し、変更に対しては機能しません。
理由は何ですか?
CodePlex(Brandon Haynes提供)にある「EntityFramework Runtime ModelAdapter」という名前の素晴らしいライブラリを使用してこの問題を解決することができました。ここから入手できます: http://efmodeladapter.codeplex.com/
ニーズに合わせて、デザイナーコードをまったく置き換える必要がないように、少し調整しました。
だから、私は元気です。
とにかく、そして特にブランドンに感謝します、素晴らしい仕事です!
さて、私はインターネット全体でこのコードを探していました。結局、私はそれを自分でしなければなりませんでした。 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モデルがこの関数によって変更されるように変更できます。
Postgresデータベースからデータをインポートする必要があります。デフォルトでは、スキーマ「public」を使用します。そのため、Entity Framework CTP4の「コードファースト」を使用します。デフォルトでは、スキーマ「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();
}
それ自体は答えではありませんが、DbContextからの使用方法を示すJanMatousekのCreate [EntityConnection]メソッドのフォローアップです。注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);
}
OData Data ServiceでEF6を使用するときにこれを機能させるのに多くの問題があったため、別の解決策を見つける必要がありました。私の場合、その場でそれを行う必要はありませんでした。一部のテスト環境やインストーラーにデプロイするときに、スキーマの変更を回避できました。
Mono.Cecil を使用して、埋め込まれた.ssdl
リソースをDLLに直接書き換えます。私の場合、これは問題なく機能します。
これを行う方法の簡単な例を次に示します。
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);
}
ソリューションをJanMatousekからエンティティフレームワーク6を使用してvb.net2013で機能するように変換することができました。また、vb.netでコードを使用する方法についても説明します。
環境(TESTDTA、CRPDTA、PRODDTA)ごとに異なるスキーマを使用するJDEdwardsデータベースがあります。これにより、環境を変更する場合に.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
それが同じ問題を抱えている人の助けになることを願っています。