我希望能够在运行时更改类映射到的表,如果所有映射都是用属性定义的,则无法实现此目标。因此,是否有一种方法可以在代码中动态定义映射。
(我不想维护XML映射文件。)
(我不想维护XML映射文件。)
假设我有两个表:
- OldData
- NewData
有时我想查询OldData,有时我想查询NewData。我希望使用相同的代码来构建这两种情况下的查询。
另请参阅 "如何动态地将实体框架模型映射到表名"。
假设我有两个表:
有时我想查询OldData,有时我想查询NewData。我希望使用相同的代码来构建这两种情况下的查询。
另请参阅 "如何动态地将实体框架模型映射到表名"。
Meta***
类来完成。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个属性/方法,我已经排除了那些只是返回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
)测试过了。实际上,很可能在我的基本测试中错过了一些东西。哦,而且只有当两个表完全相同,包括列名时,这才能起作用。ExecuteQuery
、ExecuteCommand
和 ExecuteMethodCall
进行解决。如果你只需要进行少量的“动态”查询,这也可以作为一种解决方法。我仍然节省了大量时间来处理其余部分的 L2S。不要把孩子和洗澡水一起倒掉;微软花了很多心思让 L2S 能够与“原始 SQL”很好地协作。 - Aaronaught公共类QueryBuilder where T: Class {
}
我编写了一个扩展方法,可以在运行时更改表名。它是通过反射完成的,因此不是真正受支持的,但它确实有效。希望它有所帮助。
Dim table As MetaTable = ctx.Mapping.GetTable(GetType(TLinqType))
table.SetTableName("someName")
<Extension()> _
Public Sub SetTableName(ByVal table As MetaTable, ByVal newName As String)
Try
'get the FieldInfo object via reflection from the type MetaTalbe
Dim tableNameField As FieldInfo = table.GetType().FindMembers(MemberTypes.Field, BindingFlags.NonPublic Or BindingFlags.Instance, Function(member, criteria) member.Name = "tableName", Nothing).OfType(Of FieldInfo)().FirstOrDefault()
'check if we found the field
If tableNameField Is Nothing Then
Throw New InvalidOperationException("Unable to find a field named 'tableName' within the MetaTable class.")
End If
'get the value of the tableName field
Dim tableName As String = TryCast(tableNameField.GetValue(table), [String])
If String.IsNullOrEmpty(tableName) Then
Throw New InvalidOperationException("Unable to obtain the table name object from the MetaTable: tableName field value is null or empty.")
End If
'set the new tableName
tableNameField.SetValue(table, newName)
Catch ex As Exception
Throw New ApplicationException(String.Format("Error setting tablename ({0}) for entity {1}!", newName, table), ex)
End Try
End Sub