2010-04-29 54 views
3

我希望能够在运行时更改类映射到的表,如果所有映射都使用属性定义,则无法执行此操作。因此有没有办法在代码中定义运行时的映射。如何在代码中定义/更改Linq To Sql的映射

(我宁可不要保持XML映射文件。)


说我有两个表:

  • OLDDATA
  • NewData

有时我希望查询OldData和我希望查询NewData的其他时间。我想在这两种情况下使用相同的代码来构建查询。


参见 “How to map an Entity framework model to a table name dynamically

+1

请您澄清一下吗? – 2010-04-29 15:22:54

+0

没有选择转移到EF? – 2010-05-06 07:35:18

回答

3

为了使这是真正的透明,你必须跳过一些非常疯狂的篮球,但它可以通过覆盖所有的Meta***具有您自己派生类型的类。

这对于像Castle这样的代理/方法拦截库实际上相当简单,但是假设这里是最低公分母,它基本上是一个漫长而无聊的实施每一个元方法来包装原始类型的考验,因为你可以不直接从任何属性映射类派生。

我会尽量坚持这里的重要覆盖;如果您在下面的代码中没有看到特定的方法/属性,则意味着该实现实际上是包装“内部”方法/属性并返回结果的单线程。我在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)测试这和也。在我非常基本的测试中,有可能,实际上很可能我错过了一些东西。哦,这只会工作,如果两个表字面上确切相同,列名和所有。

如果此表有任何关联(外键),可能会搞砸,因为您必须重写关联名称,两个都结束。我会把这个作为练习留给读者,因为考虑它会让我头痛。从这个特定的表格中删除任何关联可能会更好,所以您不必处理这些头痛的问题。

玩得开心!

+0

一起工作谢谢,鉴于我已被告知使用“原始sql”由其他人在项目上工作,因为他认为LinqToSql是速度较慢,并且只是重写任何LingToSql代码以使用row sql,而无需第一次了解LinqToSql发生了什么。这可能是在这里使用任何ORM系统的最后一个钉子...... – 2010-05-05 11:40:39

+2

@Ian:你意识到那些不是相互排斥的选项,对吧?我有一些相当大的项目使用Linq to SQL作为后端,但大约20-30%的功能是通过存储过程和特殊查询通过ExecuteQuery,ExecuteCommand和ExecuteMethodCall进行路由的,如果您只有少量查询需要进行“动态”处理,也可以在此处用作解决方法。剩下的时间我仍然使用L2S节省了大量的时间。不要把婴儿扔出洗澡水;微软花了很多麻烦让L2S在“原始SQL”上表现出色。 – Aaronaught 2010-05-05 12:06:31

1

使用GenericList类查询这个

公共类的QueryBuilder其中T:类 {

}

+0

这不会在.net 3.5中工作,但可能与我们移动到.net 4 – 2010-05-11 09:32:20

1

我写了一个扩展方法来在运行时更改表名。这是通过反思完成的,所以不是真的支持,但它的工作原理。希望它有帮助

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 
+0

谢谢,我还没有测试过这个项目,但是这看起来像一个可行的答案。 – 2010-05-28 08:53:00

+0

有什么办法可以将表名重设为默认值?当我做MyModelDataContext.Create()时,我改变了表格... – Elastep 2013-10-29 17:43:48