web-dev-qa-db-ja.com

実行時のスキーマ名の変更-EntityFramework

実行時にエンティティのストレージスキーマを変更する必要があります。私はここで利用できる素晴らしい投稿をフォローしました: http://blogs.Microsoft.co.il/blogs/idof/archive/2008/08/22/change-entity-framework-storage-db-schema -in-runtime.aspx?CommentPosted = true#commentmessage

これは完全に機能しますが、クエリに対してのみ機能し、変更に対しては機能しません。

理由は何ですか?

22
nirpi

CodePlex(Brandon Haynes提供)にある「EntityFramework Runtime ModelAdapter」という名前の素晴らしいライブラリを使用してこの問題を解決することができました。ここから入手できます: http://efmodeladapter.codeplex.com/

ニーズに合わせて、デザイナーコードをまったく置き換える必要がないように、少し調整しました。

だから、私は元気です。

とにかく、そして特にブランドンに感謝します、素晴らしい仕事です!

11
nirpi

さて、私はインターネット全体でこのコードを探していました。結局、私はそれを自分でしなければなりませんでした。 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モデルがこの関数によって変更されるように変更できます。

25
Jan Matousek

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();
        }
5
msa.im

それ自体は答えではありませんが、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);
}
2
Dave

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

ソリューションを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

それが同じ問題を抱えている人の助けになることを願っています。

0
Andrew Crouse