模型略有变化的多个数据库.如何允许实体框架在运行时删除列?



这是"模型略有变化的多个数据库。如何允许EF在运行时使用不同的数据库结构?"问题:

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

我正在为OracleMSSQL数据库使用不同的EF模型。

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

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.您需要将构造函数重载添加到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 是不言自明的。

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

然后,实际上做令人讨厌的事情的人,例如从实体映射xml中删除节点,然后再将它们提供给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; }
}

希望这能为您节省一些时间。

最新更新