如何我定义/更改映射的LINQ to SQL在code定义、LINQ、to、SQL

2023-09-03 02:57:42 作者:不如不见ζ

我希望能够改变表一类映射到在运行时,我不能这样做,如果所有的映射与属性的定义。因此,有没有办法在运行时code定义的映射。

I wish to be able to change the table a class is mapped to at run time, I can’t do this if all the mappings are defined with attributes. Therefore is there a way to define the mappings at runtime in code.

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

(I would rather not have to maintain xml mapping files.)

说我有两个表:

OLDDATA NewData

有时候我想查询OLDDATA和其他时间我想查询NewData。我想用同样的code建立查询在两种情况下。

and sometimes I wished to query OldData and other times I wished to query NewData. I want to use the same code to build the queries in both cases.

另请参见How映射实体框架模型,一个表名动态的

推荐答案

为了使这个真正透明的,你必须跳通过一些pretty的疯狂箍,但它可以通过覆盖所有的元*** 类与自己的派生类型。

In order to make this truly transparent, you have to jump through some pretty crazy hoops, but it can be done by overriding all the Meta*** classes with your own derived types.

这实际上是相当简单的与代理/方法拦截库像城堡,但假设最小公分母在这里,它基本上实现每一个元的方法来包装原始类型的一个漫长而乏味的考验,因为你可以'牛逼直接来源于任何的属性映射类。

This would actually be fairly straightforward with a proxy/method interception library like Castle, but assuming the lowest common denominator here, it's basically a long and boring ordeal of implementing every single meta method to wrap the original type, because you can't derive directly from any of the attribute mapping classes.

我会尽量坚持到这里的重要替代;如果你没有看到一个特别的方法/属性在下面的code,这意味着实施是从字面上只有一行,它包装的内部方法/属性并返回结果。我已经张贴了整个事情,单行法和所有的,在引擎收录这样你就可以剪切/粘贴测试/实验。

I'll try to stick to the important overrides here; if you don't see a particular method/property in the code below, it means that the implementation is literally a one-liner that wraps the "inner" method/property and returns the result. I've posted the whole thing, one-line methods and all, on PasteBin so you can cut/paste for testing/experimentation.

您需要做的第一件事是一个快速重写声明,它看起来是这样的:

The first thing you need is a quick override declaration, which looks like this:

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

现在的元类。从最低级开始,你必须实现一个元类型包装:

Now the meta classes. Starting from the lowest level, you have to implement a MetaType wrapper:

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 。还有我吗?好了,接下来就是元表

Again, you have to implement about 30 properties/methods for this, I've excluded the ones that just return innerType.XYZ. Still with me? OK, next is the 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; }
    }
}

是啊,没意思。好了,接下来就是元模型本身。在这里,事情变得更有趣,这是我们真正开始宣告覆盖:

Yup, boring. OK, next is the MetaModel itself. Here things get a little more interesting, this is where we really start declaring overrides:

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

我们几乎已经完成了!现在,你只需要映射源:

We're almost done! Now you just need the mapping 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));
    }
}

现在,我们终于可以开始使用这个(唷):

Now we can FINALLY start using this (phew):

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 )。这是可能的,实际上是相当有可能的,我已经错过了一些东西在我的非常基本的测试。哦,这只会如果两个表是字面上的工作完全一样,列名和所有的。

I've tested this with queries and also with insertions (InsertOnSubmit). It's possible, actually rather likely, that I've missed something in my very basic testing. Oh, and this will only work if the two tables are literally exactly the same, column names and all.

有可能会陷入困境,如果此表有任何关联(外键),因为你不得不重写协会名称太上的两个的结束。我会离开,作为一个练习留给读者,因为想着它使我的头很疼。你可能会更好,从这个特定的表只删除任何关联,所以你不必处理这个头痛的问题。

It will probably mess up if this table has any associations (foreign keys), since you'd have to override the association names too, on both ends. I'll leave that as an exercise to the reader, since thinking about it makes my head hurt. You'd probably be better off just removing any associations from this particular table, so you don't have to deal with that headache.

玩得开心!