多个数据库,模型略有变化。如何允许Entity Framework在运行时删除列?

时间:2016-06-22 10:26:08

标签: c# .net sql-server oracle entity-framework

这是对“Multiple databases with slightly changing models. How do I allow EF to work with different database structures at run-time?”问题的跟进:

我正在使用EF6MSSQLOracle.NET4.5在全公司(多个部门)全球使用的系统上查询属于的不同数据库我们的部门大多数都是EF模型,有些数据库是Oracle,有些数据库是Microsoft SQL,有些是开发或者是uat,有些是日志。

我为EFOracle数据库使用了不同的MSSQL模型。

一个要求是在运行时在数据库之间切换,这很容易,

public AggregatorEntities(string connectionString) : base(connectionString) { }

然而它确实有副作用 - 许多数据库(dev,uat,dr,logs,...)与Live不同(模型是从Live生成的),这在查询这些数据库时会导致错误。

管理层了解情况,他们可以在某个特定数据库上工作的开发人员对全局查询系统进行更改,以便测试人员和uat查询数据。但是,他们希望进行更改,以便花费最少的时间来完成此操作 - 因为每个涉及数据库更改的项目都需要额外的成本。我基本上需要构建一个“可以处理所有”的弹性系统,当一个人在EF中更改数据库时,会做一些事情来适应特定的数据库。

有不同的失败情况:
1.表上列的名称相同但实体中的类型不同 2.表格中没有列,但EF中有一个实体 3.表格上不在EF上的其他栏目 4.数据库中不在EF模型中的附加表
5.数据库中没有表,但EF模型中有实体。

这是在运行时删除EF模型但不在数据库中的列的情况(上面的第3点)。

1 个答案:

答案 0 :(得分:1)

好的,所以我构建了这个讨厌的东西,允许在运行时从表中删除列。

1.您需要在DbContext*.edmx - > *.Context.tt - > *.Context.cs)中添加构造函数重载,例如:

public partial class EcomEntities : DbContext
{
    public EcomEntities(DbConnection connection)
        : base(connection, true)
    {
    }

    public EcomEntities(string connectionString)
        : base(connectionString)
    {
    }

2.您将需要数据库连接更改器(为清楚起见,我在此明确地硬编码参数,通常会从SystemToDatabaseMapping获取它们)。 ColumnsToRemove包含需要删除的表名和列名,EF connectionString is self explanatory

public void ChangeConnection(SystemToDatabaseMapping systemToDatabaseMapping)
{  
    if (systemToDatabaseMapping.ColumnsToRemove != null)
        {
            var entityConnection = EntityConnectionExtensions.Create(
                new List<ColumnsToRemove> { new ColumnsToRemove("QUOTE_HOUSE", "UPRN"), new ColumnsToRemove("QUOTE_HOUSE", "INSIGHT_DATA") },
                systemToDatabaseMapping.ConnectionString);
            this.Ecom = new EcomEntities(entityConnection);
        }
        else
        {
            this.Ecom = new EcomEntities(systemToDatabaseMapping.ConnectionString);
        }
 ....
}

然后真正做出令人讨厌的事情的人就像从实体映射xmls中删除节点,然后再将它们提供给MetadataWorkspace

using System.Collections.Generic;
using System.Data.Common;
using System.Data.Entity.Core.EntityClient;
using System.Data.Entity.Core.Mapping;
using System.Data.Entity.Core.Metadata.Edm;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;

public static class EntityConnectionExtensions
{
    public static IEnumerable<XElement> ElementsAnyNS<T>(this IEnumerable<T> source, string localName)
        where T : XContainer
    {
        return source.Elements().Where(e => e.Name.LocalName == localName);
    }

    public static IEnumerable<XElement> ElementsAnyNS(this XContainer source, string localName)
    {
        return source.Elements().Where(e => e.Name.LocalName == localName);
    }

    private static void RemoveNodes(XElement element, List<ColumnsToRemove> tableAndColumn)
    {
        if (element.Attribute("Name") != null && tableAndColumn.Any(oo => oo.Table == element.Attribute("Name").Value) ||
            element.Attribute("StoreEntitySet") != null && tableAndColumn.Any(oo => oo.Table == element.Attribute("StoreEntitySet").Value))
        {
            var matchingSelectParts = tableAndColumn.Where(oo => element.Value.Contains(string.Format("\"{0}\".\"{1}\" AS \"{1}\"", oo.Table, oo.Column))).ToList();
            if (matchingSelectParts.Any())
            {
                foreach (var matchingSelectPart in matchingSelectParts)
                {
                    var definingQuery = element.ElementsAnyNS("DefiningQuery").Single();
                    definingQuery.Value = definingQuery.Value.Replace(string.Format(", \n\"{0}\".\"{1}\" AS \"{1}\"", matchingSelectPart.Table, matchingSelectPart.Column), "");
                }
            }
            else
            {
                var nodes = element.Nodes()
                    .Where(o =>
                        o is XElement
                        && ((XElement) o).Attribute("Name") != null
                        && tableAndColumn.Any(oo => ((XElement) o).Attribute("Name").Value == oo.Column));
                foreach (var node in nodes.ToList())
                {
                    node.Remove();
                }
            }
        }
    }

    public static EntityConnection Create(List<ColumnsToRemove> tablesAndColumns, string connString)
    {
        var modelNameRegex = new Regex(@".*metadata=res:\/\/\*\/([a-zA-Z.]*).csdl|.*");
        var model = modelNameRegex.Matches(connString).Cast<Match>().SelectMany(o => o.Groups.Cast<Group>().Skip(1).Where(oo => oo.Value != "")).Select(o => o.Value).First();

        var conceptualReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".csdl"));
        var mappingReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".msl"));
        var storageReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".ssdl"));

        var conceptualXml = XElement.Load(conceptualReader);
        var mappingXml = XElement.Load(mappingReader);
        var storageXml = XElement.Load(storageReader);

        foreach (var entitySet in new[] {storageXml, conceptualXml}.SelectMany(xml => xml.Elements()))
        {
            if (entitySet.Attribute("Name").Value == "ModelStoreContainer")
            {
                foreach (var entityContainerEntitySet in entitySet.Elements())
                {
                    RemoveNodes(entityContainerEntitySet, tablesAndColumns);
                }
            }

            RemoveNodes(entitySet, tablesAndColumns);
        }

        foreach (var entitySet in mappingXml.Elements().ElementAt(0).Elements())
        {
            if (entitySet.Name.LocalName == "EntitySetMapping")
            {
                foreach (var entityContainerEntitySet in entitySet.Elements().First().Elements())
                {
                    RemoveNodes(entityContainerEntitySet, tablesAndColumns);
                }
            }

            RemoveNodes(entitySet, tablesAndColumns);
        }

        var storageCollection = new StoreItemCollection(new [] {storageXml.CreateReader()});
        var conceptualCollection = new EdmItemCollection(new[] { conceptualXml.CreateReader() });
        var mappingCollection = new StorageMappingItemCollection(conceptualCollection, storageCollection, new[] {mappingXml.CreateReader()});

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

public class ColumnsToRemove
{
    public ColumnsToRemove(string table, string column)
    {
        Table = table;
        Column = column;
    }

    public string Table { get; set; }
    public string Column { get; set; }
}

public class SystemToDatabaseMapping
{
    public string ConnectionString { get; set; }
    public List<ColumnsToRemove> ColumnsToRemove  { get; set; }
}

希望这可以节省你一些时间。