为了使其真正透明,您必须跳过一些非常疯狂的环节,但可以通过使用您自己的派生类型覆盖所有 Meta*** 类来完成。
对于像 Castle 这样的代理/方法拦截库,这实际上是相当简单的,但假设这里的公分母最低,实现每个元方法来包装原始类型基本上是一个漫长而无聊的考验,因为你可以' t 直接派生自任何属性映射类。
我会尽量坚持这里的重要覆盖;如果您在下面的代码中没有看到特定的方法/属性,则意味着该实现实际上是一个单行代码,它包装了“内部”方法/属性并返回结果。我已经在PasteBin 上发布了整个内容、单行方法和所有内容,因此您可以剪切/粘贴以进行测试/实验。
您需要的第一件事是快速覆盖声明,如下所示:
class TableOverride
{
public TableOverride(Type entityType, string tableName)
{
if (entityType == null)
throw new ArgumentNullException("entityType");
if (string.IsNullOrEmpty(tableName))
throw new ArgumentNullException("tableName");
this.EntityType = entityType;
this.TableName = tableName;
}
public Type EntityType { get; private set; }
public string TableName { get; private set; }
}
现在是元类。从最底层开始,你必须实现一个MetaType 包装器:
class OverrideMetaType : MetaType
{
private readonly MetaModel model;
private readonly MetaType innerType;
private readonly MetaTable overrideTable;
public OverrideMetaType(MetaModel model, MetaType innerType,
MetaTable overrideTable)
{
if (model == null)
throw new ArgumentNullException("model");
if (innerType == null)
throw new ArgumentNullException("innerType");
if (overrideTable == null)
throw new ArgumentNullException("overrideTable");
this.model = model;
this.innerType = innerType;
this.overrideTable = overrideTable;
}
public override MetaModel Model
{
get { return model; }
}
public override MetaTable Table
{
get { return overrideTable; }
}
}
同样,您必须为此实现大约 30 个属性/方法,我已经排除了仅 return innerType.XYZ 的那些。还在我这儿?好的,接下来是MetaTable:
class OverrideMetaTable : MetaTable
{
private readonly MetaModel model;
private readonly MetaTable innerTable;
private readonly string tableName;
public OverrideMetaTable(MetaModel model, MetaTable innerTable,
string tableName)
{
if (model == null)
throw new ArgumentNullException("model");
if (innerTable == null)
throw new ArgumentNullException("innerTable");
if (string.IsNullOrEmpty(tableName))
throw new ArgumentNullException("tableName");
this.model = model;
this.innerTable = innerTable;
this.tableName = tableName;
}
public override MetaModel Model
{
get { return model; }
}
public override MetaType RowType
{
get { return new OverrideMetaType(model, innerTable.RowType, this); }
}
public override string TableName
{
get { return tableName; }
}
}
是的,无聊。好的,接下来是MetaModel 本身。这里的事情变得更有趣了,这就是我们真正开始声明覆盖的地方:
class OverrideMetaModel : MetaModel
{
private readonly MappingSource source;
private readonly MetaModel innerModel;
private readonly List<TableOverride> tableOverrides = new
List<TableOverride>();
public OverrideMetaModel(MappingSource source, MetaModel innerModel,
IEnumerable<TableOverride> tableOverrides)
{
if (source == null)
throw new ArgumentNullException("source");
if (innerModel == null)
throw new ArgumentNullException("innerModel");
this.source = source;
this.innerModel = innerModel;
if (tableOverrides != null)
this.tableOverrides.AddRange(tableOverrides);
}
public override Type ContextType
{
get { return innerModel.ContextType; }
}
public override string DatabaseName
{
get { return innerModel.DatabaseName; }
}
public override MetaFunction GetFunction(MethodInfo method)
{
return innerModel.GetFunction(method);
}
public override IEnumerable<MetaFunction> GetFunctions()
{
return innerModel.GetFunctions();
}
public override MetaType GetMetaType(Type type)
{
return Wrap(innerModel.GetMetaType(type));
}
public override MetaTable GetTable(Type rowType)
{
return Wrap(innerModel.GetTable(rowType));
}
public override IEnumerable<MetaTable> GetTables()
{
return innerModel.GetTables().Select(t => Wrap(t));
}
private MetaTable Wrap(MetaTable innerTable)
{
TableOverride ovr = tableOverrides.FirstOrDefault(o =>
o.EntityType == innerTable.RowType.Type);
return (ovr != null) ?
new OverrideMetaTable(this, innerTable, ovr.TableName) :
innerTable;
}
private MetaType Wrap(MetaType innerType)
{
TableOverride ovr = tableOverrides.FirstOrDefault(o =>
o.EntityType == innerType.Type);
return (ovr != null) ?
new OverrideMetaType(this, innerType, Wrap(innerType.Table)) :
innerType;
}
public override MappingSource MappingSource
{
get { return source; }
}
}
我们快完成了!现在你只需要映射源:
class OverrideMappingSource : MappingSource
{
private readonly MappingSource innerSource;
private readonly List<TableOverride> tableOverrides = new
List<TableOverride>();
public OverrideMappingSource(MappingSource innerSource)
{
if (innerSource == null)
throw new ArgumentNullException("innerSource");
this.innerSource = innerSource;
}
protected override MetaModel CreateModel(Type dataContextType)
{
var innerModel = innerSource.GetModel(dataContextType);
return new OverrideMetaModel(this, innerModel, tableOverrides);
}
public void OverrideTable(Type entityType, string tableName)
{
tableOverrides.Add(new TableOverride(entityType, tableName));
}
}
现在我们终于可以开始使用这个了(呸):
var realSource = new AttributeMappingSource();
var overrideSource = new OverrideMappingSource(realSource);
overrideSource.OverrideTable(typeof(Customer), "NewCustomer");
string connection = Properties.Settings.Default.MyConnectionString;
using (MyDataContext context = new MyDataContext(connection, overrideSource))
{
// Do your work here
}
我已经通过查询和插入 (InsertOnSubmit) 对此进行了测试。有可能,实际上很有可能,我在非常基本的测试中遗漏了一些东西。哦,这只有在两个表字面上完全相同、列名和所有内容都相同的情况下才有效。
如果这个表有任何关联(外键),它可能会搞砸,因为您也必须在 both 两端覆盖关联名称。我将把它作为练习留给读者,因为想想它会让我头疼。您最好从这个特定的表中删除任何关联,这样您就不必处理那个令人头疼的问题。
玩得开心!