Уровень доступа к данным - LINQ-To-SQL и дженерики.Могу ли я оптимизировать это? - PullRequest
2 голосов
/ 16 августа 2010

Мы работаем над улучшением нашего DAL, написанного на LINQ, который взаимодействует с базой данных MS SQL. Наша цель - добиться хорошего повторного использования, используя как можно меньше кода.

Файлы, сгенерированные LINQ, используют обобщенные элементы и рефлексию для отображения классов, сгенерированных LINQ, в объекты SQL (в нашем случае таблицы и представления).

Пожалуйста, посмотрите пример существующего средства доступа. Этот метод находится в частичном классе, который содержит пользовательские конструкторы, методы доступа и мутаторы:

public clsDVD getDVD(int dvdId)
{
   try
   {
      using (DataContext dvdDC = new DataContext(ConnectionStringManager.getLiveConnStr()))
      {
                    // Deferred loading
                    dvdDC.DeferredLoadingEnabled = false;

                    var tDVD = dvdDC.GetTable<DVD>();

                    return (from t in tDVD 
                            // Filter on DVD Id
                            where t.DVDId == (dvdId)
                            select t).Single();                   
      }
   catch (Exception e)
   {
       Logger.Log("Can't get requested DVD.", e);
       throw;
   }
 }

Я считаю, что это очень легко поддерживать, так как большая часть работы выполняется после var tDVD

Было предложено вообще не объявлять tDVD и использовать dataContext.TableName, но за кулисами он все еще называет GetTable<>.

Единственный способ улучшить это - разбить этот частичный класс на 4 (CRUD) частичных класса. Э.Г.

clsDVD_Select, clsDVD_Update, clsDVD_Insert, clsDVD_Delete

В этом случае каждый класс будет представлять набор поведений.

Идея, которую мы обсуждаем, состоит в том, чтобы посмотреть, возможно ли использовать дженерики поверх дженериков LINQ.

Например, вместо частичных классов мы выясняем свойства класса на ходу, используя отражение в базе данных SQL. Мое первое беспокойство здесь - это влияние на производительность. Насколько это будет значительным.

Вместо ClsDVD.getDVD(1231) у нас будет что-то вроде: GenericDC.Select<DVD>(1231)

.Select метод определит первичный ключ и выполнит запрос выбора для этой таблицы. Я изо всех сил пытаюсь понять, как это может работать. Допустим, мы можем заставить это работать для простого выбора, то есть выбора с фильтром по первичному ключу, но что произойдет, когда мы начнем делать сложные объединения и группировать по выборкам. Что происходит, когда мы хотим иметь несколько вариантов выбора для одного класса DVD?

Мое последнее беспокойство связано с хорошими практиками. Мне уже говорили, что хорошо иметь постоянный код. Например, если я решу использовать таблицы данных, я должен придерживаться таблиц данных на протяжении всего проекта. Это плохая идея иметь половину проекта с таблицами данных и другую половину с пользовательскими классами. Вы согласны с этим?

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

Спасибо

1 Ответ

1 голос
/ 16 августа 2010

Вот один из способов сделать ситуацию немного более общей.Нажмите и повторите для других операций CRUD.Для некоторых ситуаций производительность может быть неприемлемой.В этих случаях я реструктурировал бы эту часть программы так, чтобы она называлась неуниверсальной версией.

public T GetSingleItem(Func<T,bool> idSelector ) where T : ??? // forgot what type it needs to be off the top of my head
{ 
    try 
    { 
        using (DataContext context = new DataContext(ConnectionStringManager.getLiveConnStr())) 
        { 
            context.DeferredLoadingEnabled = false; 
            return context.GetTable<T>().Single( item => idSelector( item );
        } 
    }
    catch (Exception e) 
    { 
        Logger.Log("Can't get requested item.", e); 
        throw; 
    } 
} 

Вот как вы должны получить этот предмет.Не совсем элегантно, потому что вы должны указать универсальной функции, какой столбец вы собираетесь использовать.

GenericDC.GetSingleItem<DVD>( dvd => dvd.ID == 1231 )

Чтобы сделать это еще более универсальным, ограничив его одним элементом с идентификатором ...

public IEnumerable<T> GetItems(Func<T,bool> selectFunction ) where T : ??? // forgot what type it needs to be off the top of my head
{ 
    try 
    { 
        using (DataContext context = new DataContext(ConnectionStringManager.getLiveConnStr())) 
        { 
            context.DeferredLoadingEnabled = false; 
            return context.GetTable<T>().Select( item => selectFunction( item );
        } 
    }
    catch (Exception e) 
    { 
        Logger.Log("Can't get requested item.", e); 
        throw; 
    } 
} 

Затем вы можете назвать его следующим образом:

GenericDC.GetItems<DVD>( dvd => dvd.Title == "Title" && dvd.Cast.Contains( "Actor" ) );

Другое возможное решение - создать собственный генератор кода, который вы можете изменить в одном месте, и создать аналогичные процедуры для всехдругие типы.Это, вероятно, будет хорошим решением, если у вас возникнут проблемы с производительностью.Вы хотели бы ограничить изменения в фрагменте кода, который вы используете.

...