Динамические таблицы из хранимой процедуры с использованием Linq to Entities - PullRequest
0 голосов
/ 27 октября 2010

У меня есть вопрос о Entity Framework и Linq to Entities.Моя версия .NET - 4.0.Я делаю рефакторинг уровня доступа к базе данных существующего приложения и планирую использовать Linq to Entities (вместо сегодняшних DataReaders и строк SQL).Структура базы данных не может быть изменена.

Моя проблема связана с хранимой процедурой, которая в упрощенном виде выглядит следующим образом:

CREATE PROCEDURE getElement @tableid as int, @elementid as int AS
BEGIN
DECLARE @tablename as varchar(50)
SELECT @tablename = tablename FROM tables WHERE tableid = @tableid
EXEC('SELECT * FROM ' + @tablename + ' WHERE elementid = ' + @elementid)
END

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

Сегодня это решается с помощью SqlDataReader, который выполняет поиск словаря в виде словаря. elemendid element.

public Element getElement(SqlDataReader dr)
{
    switch((int)dr["elementid"])
    {
        case 1:
            return getTextElement(dr);
        case 2:
            return getImageElement(dr);
        //...
    }
}

Element является абстрактным базовым классом.getTextElement возвращает TextElement : Element, а getImageElement возвращает ImageElement : Element.

Как мне смоделировать это в Entity Framework?Сложные типы, кажется, не сокращают его, так как он не поддерживает динамические свойства.Я также посмотрел на EntityObject Generator, но на самом деле я не настолько опытен в настройке кода T4 (может, мне стоит изучить эту проблему?).Идеальным решением для меня было бы, чтобы импортированная хранимая процедура возвращала объект с типом dynamic , но Entity Framework 4, похоже, не поддерживает это.

Ответы [ 2 ]

0 голосов
/ 20 ноября 2010

Я просто подумал, что добавлю, как я решил это.

Я создал пару вспомогательных классов, которые имитируют поведение Linq to Entities, и использую их в своих специальных хранимых процедурах. Он далек от совершенства или даже хорош, но в результате код выглядит очень похожим на Linq to Entities. Это важно для меня, так как остальная часть моего уровня базы данных будет использовать Linq to Entities.

В идеальном мире я мог бы сформулировать запрос к Linq to Entities, а затем использовать результат, несколько похожий на то, что я делаю сейчас.

Вот и мы ...

Код используется следующим образом:

var connectionString = new SqlConnectionStringBuilder
{
    DataSource = @"C:\Temp\Northwind.mdf"
};

var commandText = "select * from Customers";

using (var rows = new SqlCommandHelper(connectionString.ToString(), System.Data.CommandType.Text, commandText))
{
    foreach (dynamic row in rows)
    {
        try
        {
            Console.WriteLine(row.Fax ?? "Emtpy");
        }
        catch (IndexOutOfRangeException)
        {
            Console.WriteLine("Invalid column name");
        }
    }
}

Как видите, перечисление строк выглядит примерно так, как если бы я использовал Linq to Entities вместо SqlCommandHelper.

Класс SqlCommandHelper представляет собой следующий код:

class SqlCommandHelper : IEnumerable<DynamicSqlRow>, IDisposable
{
    private SqlConnection connection;
    private SqlCommand command;

    public SqlCommandHelper(string connectionString, System.Data.CommandType commandType, string commandText, params SqlParameter[] parameters)
    {
        connection = new SqlConnection(connectionString);
        command = new SqlCommand
        {
            CommandText = commandText,
            CommandType = commandType,
            Connection = connection
        };

        command.Parameters.AddRange(parameters);
    }

    public IEnumerator<DynamicSqlRow> GetEnumerator()
    {
        if (connection.State != System.Data.ConnectionState.Open)
        {
            connection.Open();
        }

        using (var reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                yield return new DynamicSqlRow(reader);
            }
        }            
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Dispose()
    {
        command.Dispose();
        connection.Dispose();
    }
}

Как видите, магия находится внутри DynamicSqlRow. Хочу отметить, что для компиляции вам нужно импортировать пространство имен System.Dynamic для DynamicSqlRow.

class DynamicSqlRow : DynamicObject
{
    System.Data.IDataReader reader;

    public DynamicSqlRow(System.Data.IDataReader reader)
    {
        this.reader = reader;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var row = reader[binder.Name];

        result = row is DBNull ? null : row;

        return true;
    }
}

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

Полезной ссылкой для меня была Пошаговое руководство. Создание и использование динамических объектов из MSDN.

Берегите себя

0 голосов
/ 27 октября 2010

Я думаю, что проблема, с которой вы сталкиваетесь, заключается в том, что разработчики EF и Linq to Sql генерируют модели на основе известной структуры таблицы.Вы выполняете оценку результата, используя EXEC, поэтому процедура не может быть проанализирована, чтобы выяснить, как будет выглядеть модель результата.Я не уверен, что это можно решить с помощью инструмента ORM, вам может потребоваться специализировать две хранимые процедуры, одну для явного возврата TextElement моделей и одну для ImageElement моделей.

...