LINQ Generic Query с унаследованным базовым классом? - PullRequest
2 голосов
/ 08 апреля 2010

Я пытаюсь написать несколько общих запросов LINQ для моих сущностей, но у меня возникают проблемы при выполнении более сложных задач. Прямо сейчас я использую класс EntityDao, который имеет все мои дженерики, и каждый из моих классов объектов Daos (например, Accomplishments Dao) наследует его, например:

using LCFVB.ObjectsNS;
using LCFVB.EntityNS;

namespace AccomplishmentNS
{
  public class AccomplishmentDao : EntityDao<Accomplishment>{}
}

Теперь мой entityDao имеет следующий код:

using LCFVB.ObjectsNS;
using LCFVB.LinqDataContextNS;

namespace EntityNS
{
public abstract class EntityDao<ImplementationType> where ImplementationType : Entity
{
public ImplementationType getOneByValueOfProperty(string getProperty, object getValue)
{ 
  ImplementationType entity = null;
         if (getProperty != null && getValue != null) {
            //Nhibernate Example:
            //ImplementationType entity = default(ImplementationType);
            //entity = Me.session.CreateCriteria(Of ImplementationType)().Add(Expression.Eq(getProperty, getValue)).UniqueResult(Of InterfaceType)()

            LCFDataContext lcfdatacontext = new LCFDataContext();

            //Generic LINQ Query Here
            lcfdatacontext.GetTable<ImplementationType>();


            lcfdatacontext.SubmitChanges();

            lcfdatacontext.Dispose();
        }


        return entity;
    }

    public bool insertRow(ImplementationType entity)
    {
        if (entity != null) {
            //Nhibernate Example:
            //Me.session.Save(entity, entity.Id)
            //Me.session.Flush()

            LCFDataContext lcfdatacontext = new LCFDataContext();

            //Generic LINQ Query Here
            lcfdatacontext.GetTable<ImplementationType>().InsertOnSubmit(entity);

            lcfdatacontext.SubmitChanges();
            lcfdatacontext.Dispose();

            return true;
        }
        else {
            return false;
        }
    }

}
}

Я получил работающую функцию insertRow, однако я даже не уверен, как поступить с getOnebyValueOfProperty, самая близкая вещь, которую я мог найти на этом сайте, была:

Общий LINQ TO SQL-запрос

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

Наконец, мне нужен какой-то способ установки нового объекта в качестве типа возвращаемого значения, установленного для типа реализации, в nhibernate (из которого я пытаюсь преобразовать) это была просто эта строка, которая сделала это:

  ImplentationType entity = default(ImplentationType);

Однако по умолчанию это команда nhibernate, как бы я это сделал для LINQ?

EDIT:

getOne не работает даже при выходе из базового класса (это частичный класс автоматически сгенерированных классов LINQ). Я даже удалил дженерики. Я попробовал:

namespace ObjectsNS
{    
    public partial class Accomplishment
     {
       public Accomplishment getOneByWhereClause(Expression<Action<Accomplishment, bool>> singleOrDefaultClause)
     {
        Accomplishment entity = new Accomplishment();
         if (singleOrDefaultClause != null) {
              LCFDataContext lcfdatacontext = new LCFDataContext();

                 //Generic LINQ Query Here
              entity = lcfdatacontext.Accomplishments.SingleOrDefault(singleOrDefaultClause);

               lcfdatacontext.Dispose();
               }
            return entity;
               }
          }
      }

Получите следующую ошибку:

Error   1   Overload resolution failed because no accessible 'SingleOrDefault' can be called with these arguments:
Extension method 'Public Function SingleOrDefault(predicate As System.Linq.Expressions.Expression(Of System.Func(Of Accomplishment, Boolean))) As Accomplishment' defined in 'System.Linq.Queryable': Value of type 'System.Action(Of System.Func(Of LCFVB.ObjectsNS.Accomplishment, Boolean))' cannot be converted to 'System.Linq.Expressions.Expression(Of System.Func(Of LCFVB.ObjectsNS.Accomplishment, Boolean))'.
Extension method 'Public Function SingleOrDefault(predicate As System.Func(Of Accomplishment, Boolean)) As Accomplishment' defined in 'System.Linq.Enumerable': Value of type 'System.Action(Of System.Func(Of LCFVB.ObjectsNS.Accomplishment, Boolean))' cannot be converted to 'System.Func(Of LCFVB.ObjectsNS.Accomplishment, Boolean)'.     14  LCF

Хорошо, нет проблем, я изменил:

public Accomplishment getOneByWhereClause(Expression<Action<Accomplishment, bool>> singleOrDefaultClause)

до:

public Accomplishment getOneByWhereClause(Expression<Func<Accomplishment, bool>> singleOrDefaultClause)

Ошибка исчезает. Хорошо, но теперь, когда я пытаюсь вызвать метод через:

Accomplishment accomplishment = new Accomplishment();
var result = accomplishment.getOneByWhereClause(x=>x.Id = 4)

Это не работает, говорит, что x не объявлено.

я тоже пробовал

 getOne<Accomplishment>
 Expression<Func<
 Expression<Action<

в различных форматах, но либо параметры не распознаются правильно как выражение в вызове функции, либо он не может преобразовать тип, который у меня есть в качестве параметра, в тип, используемый внутри singleofDefault (). Так что обе ошибки, как и выше. И у достижения класса есть идентификатор. Наконец, я также попытался объявить x новым достижением, чтобы оно было объявлено, после чего код автоматически изменяет => на> = и говорит:

Error   1   Operator '>=' is not defined for types 'LCFVB.ObjectsNS.Accomplishment' and 'Integer'.  

= (

1 Ответ

2 голосов
/ 08 апреля 2010

Если я понимаю, что вы хотите, вопрос, на который вы ссылаетесь, описывает (вроде), что вам нужно делать.

public ImplementationType getOne(Expression<Func<ImplementationType , bool> singleOrDefaultClause)
{ 
    ImplementationType  entity = null;
    if (singleOrDefaultClause != null) 
    {

        LCFDataContext lcfdatacontext = new LCFDataContext();

        //Generic LINQ Query Here
        entity = lcfdatacontext.GetTable<ImplementationType>().SingleOrDefault(singleOrDefaultClause);


        lcfdatacontext.Dispose();
    }


    return entity;
}

Когда вы вызываете этот метод, он будет выглядеть примерно так:

//note assumption that ConcreteClass DAO has member called Id
var myEntity = ConcreteClass.getOne(x=>x.Id == myIdVariable);

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

Обновление Разве вы не можете просто использовать new для создания экземпляра нужного вам класса? Если вам нужно что-то более общее, то я думаю, что вам придется использовать рефлексию для вызова конструктора. Извините, если я неправильно понял, что вы спрашивали.

Обновление в ответ на комментарий для дополнительной информации Расширение обновления POCO: Есть много способов сделать это, но один из них - получить PropertyInfo из выражения и вызвать установщик. (Возможно, более эффективные способы сделать это, но я не понял один.) Например, это может выглядеть примерно так:

protected internal bool Update<TRet>(Expression<Func<T, TRet>> property, TRet updatedValue)
{
    var property = ((MemberExpression)member.Body).Member as PropertyInfo;
    if (property != null)
    {
        property.SetValue(this, updatedValue, null);
        return true;
    }
    return false;
}

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

Мои методы работают, но ваши мне нравятся больше, потому что они более гибкие, допускают несколько параметров, но я действительно очень хочу оставить это в моем DAO. Было бы немного странно иметь все методы db в моем DAO, кроме одного. Будет ли установка функции getOne работать?

Я не совсем уверен, что понимаю, о чем вы меня спрашиваете. Да, вы могли бы установить функцию getOne как универсальный метод, это фактически то, что я сделал, хотя я сделал еще один шаг, и все методы являются универсальными. Это упростило мою границу интерфейса UI / BL и, по крайней мере, пока достаточно выразительно / гибко, чтобы покрыть все мои требования к использованию без серьезных изменений. Если это поможет, я включил интерфейс, который BL-объект реализует и предоставляет для пользовательского интерфейса. Мой DAL по сути является NHibernate, поэтому мне нечего вам там показать.

public interface ISession : IDisposable
{
    bool CanCreate<T>() where T : class,IModel;
    bool CanDelete<T>() where T : class, IModel;
    bool CanEdit<T>() where T : class, IModel;
    bool CanGet<T>() where T : class, IModel; 

    T Create<T>(IEditable<T> newObject) where T:class,IModel;

    bool Delete<T>(Expression<Func<T, bool>> selectionExpression) where T : class, IModel;

    PageData<T> Get<T>(int page, int numberItemsPerPage, Expression<Func<T, bool>> whereExpression) where T : class, IModel;
    PageData<T> Get<T, TKey>(int page, int numberItemsPerPage, Expression<Func<T, bool>> whereExpression, Expression<Func<T, TKey>> orderBy, bool isAscending) where T : class, IModel;

    T Get<T>(Expression<Func<T, bool>> selectionExpression) where T : class, IModel;

    IEnumerable<T> GetAllMatches<T>(Expression<Func<T, bool>> whereExpression) where T : class, IModel;

    IEditable<T> GetForEdit<T>(Expression<Func<T, bool>> selectionExpression) where T : class, IModel;

    IEditable<T> GetInstance<T>() where T : class, IModel;

    IQueryable<T> Query<T>() where T : class, IModel;

    bool Update<T>(IEditable<T> updatedObject) where T : class, IModel;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...