Как определить / изменить сопоставления для Linq To Sql в коде - PullRequest
3 голосов
/ 29 апреля 2010

Я хочу иметь возможность изменять таблицу, в которую отображается класс во время выполнения, я не могу этого сделать, если все отображения определены с атрибутами. Поэтому есть способ определить сопоставления во время выполнения в коде.

(Я бы предпочел не поддерживать файлы сопоставления XML).


Скажем, у меня есть две таблицы:

  • OldData
  • NewData

и иногда я хотел запросить OldData, а иногда я хотел запросить NewData. Я хочу использовать один и тот же код для построения запросов в обоих случаях.


См. Также " Как динамически сопоставить модель платформы Entity с именем таблицы "

Ответы [ 3 ]

3 голосов
/ 05 мая 2010

Чтобы сделать это по-настоящему прозрачным, вы должны перепрыгнуть через несколько довольно сумасшедших обручей, но это можно сделать, переопределив все классы 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). Возможно, на самом деле довольно вероятно, что я что-то пропустил в самом базовом тестировании. О, и это будет работать, только если две таблицы буквально точно одинаковы, имена столбцов и все.

Вероятно, возникнет ошибка, если в этой таблице есть какие-либо ассоциации (внешние ключи), поскольку вам также придется переопределять имена ассоциаций, в оба заканчиваются. Я оставлю это в качестве упражнения для читателя, так как от размышлений об этом у меня болит голова. Вам, вероятно, было бы лучше просто удалить любые ассоциации из этой конкретной таблицы, поэтому вам не придется иметь дело с этой головной болью.

Веселись!

1 голос
/ 27 мая 2010

Я написал метод расширения для изменения имени таблицы во время выполнения. Это делается отражением, поэтому не очень поддерживается, но работает. Надеюсь, это поможет

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
1 голос
/ 30 апреля 2010

Использование GenericList для класса для запроса этого

Открытый класс Querybuilder, где T: Class {

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...