Изменение имени схемы во время выполнения - Entity Framework - PullRequest
21 голосов
/ 18 апреля 2010

Мне нужно изменить схему хранения сущностей во время выполнения. Я подписался на замечательный пост, доступный здесь: http://blogs.microsoft.co.il/blogs/idof/archive/2008/08/22/change-entity-framework-storage-db-schema-in-runtime.aspx?CommentPosted=true#commentmessage

Это прекрасно работает, но только для запросов, а не для модификаций.

Есть идеи почему?

Ответы [ 6 ]

24 голосов
/ 06 марта 2013

Ну, я искал этот кусок кода по всему Интернету. В конце концов я должен был сделать это сам. Он основан на адаптере Брэндона Хейнса, но эта функция - все, что вам нужно для изменения схемы во время выполнения, и вам не нужно заменять автоматически сгенерированные конструкторы контекста.

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, а не только указанную.

11 голосов
/ 23 апреля 2010

Мне удалось решить эту проблему с помощью великолепной библиотеки, расположенной в CodePlex (любезно предоставленной Брэндоном Хейнсом), которая называется «Адаптер модели среды выполнения Entity Framework» и доступна здесь: http://efmodeladapter.codeplex.com/

Я немного подправил его, чтобы он соответствовал нашим потребностям и вообще не нуждался в замене кода дизайнера.

Итак, я в порядке.

В любом случае, спасибо, и особенно Брэндону, потрясающая работа!

5 голосов
/ 16 ноября 2010

Мне нужно импортировать данные из базы данных postgres. По умолчанию используется схема «public». Поэтому я использую Entity Framework CTP 4 «Сначала код». По умолчанию используется схема "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();
        }
2 голосов
/ 08 июля 2014

Не ответ как таковой, а продолжение метода Яна Матауска 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 голосов
/ 30 июня 2016

У меня было много проблем, чтобы заставить это работать при использовании EF6 с OData Data Service, поэтому мне пришлось искать альтернативное решение. В моем случае мне не нужно было делать это на лету. Я мог бы избежать изменения схемы при развертывании в некоторых тестовых средах и в установщике.

Используйте 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 голосов
/ 24 сентября 2014

Мне удалось преобразовать решение от Jan Matousek для работы в vb.net 2013 с каркасом сущностей 6. Я также попытаюсь объяснить, как использовать код в vb.net.

У нас есть база данных JD Edwards, которая использует разные схемы для каждой среды (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

Надеюсь, это поможет кому-то там с такими же проблемами.

...