Настройка IQueryable <T> - PullRequest
       33

Настройка IQueryable <T>

3 голосов
/ 28 января 2009

Я пытаюсь настроить объекты моего приложения, чтобы они имели свойство, ссылающееся на DataContext, который их загрузил.

Я думаю, что лучший способ - как-то создать класс, который реализует IQueryable, и установить свойство entity datacontext в его методе GetEnumerator.

У меня вопрос: как я могу использовать Провайдера и Выражение, используемые Linq для SQL в моей реализации IQueryable, чтобы мне не пришлось реализовывать их самостоятельно?

Кстати: для моего сценария есть другой способ?

Взгляните на следующий код:

public partial class Product: IEntityBase  
{ 

    public Product() 
    { 
        _DataContext = new SampleDataContext(); 
    } 

    private long _Id;  
    [Column(Storage="_Id", AutoSync=AutoSync.OnInsert, DbType="BigInt NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]  
    public long Id  
    {  
         get{ return _Id; }  
         set{ _Id = value; }  
    }  

    private string _Name;  
    [Column(Storage="_Name", DbType="NVarChar(MAX) NOT NULL", CanBeNull=false  
    public string Name  
    {  
        get{ return _Name; }  
        set{ _Name = value; }  
    }  

    private SampleDataContext _DataContext;  

    //This is the property extending the Product class and should be set when this class is being returned 
    //by IQueryable<T>.GetEnumerator() 
    public SampleDataContext DataContext  
    {  
        get{ return _Name; }  
        set{ _Name = value; }  
    }  

    public MyQueryable<Product> GetProducts() 
    { 
        MyQueryable<Product> result = from p in context.Products 
                                      where {Some Conditions 1} 
                                      select p; 
        result.DataContext = _DataContext; 
        return result; 
    } 

    public void SomeMethod() 
    { 
        //This query will not actually set the DataCotnext property. 
        //And the generated sql query is something like:  
        //SELECT * FROM Products WHERE {Some Conditions 1} AND {Some Conditions 2} 
        var products = GetProducts().Where( {Some Conditions 2} ); 

        //Now that the GetEnumerator() is called the DataContext property of the products 
        //will be set. 
        foreach( var item in products ) 
        { 
            item.Name = "Test Name"; 
            item.DataContext.SubmitChanges(); 
        } 
    } 
}  

public MyQueryable<T>: IQueryable<T> 
    where T: class, IEntityBase 
{ 
    // 
    //Implementation of IQueryable which is my question 
   // 

   public IEnumerator<T> GetEnumerator() 
   { 
       foreach( var item in Provider.GetEnumerator<T>() ) 
       { 
            item.DataContext = this.DataContext; 
            yield return item; 
       } 
   } 

   public SampleDataContext DataContext{ get; set; } 
} 

public interface IEntityBase 
{ 
    SampleDataContext DataContext{ get; set; }; 
} 

UPDATE

Я нашел ответ сам. Вот пример кода, показывающий, как я это сделал.

public MyQueryable<T, TContext>: IQueryable<T> 
    where T: class, IEntityBase 
    where TContext: DataContext, new()
{ 

   public MyQueryable<T>(TContext context, IQueryable<T> baseIQueryable)
   {
        if( baseIQueryable == null )
            throw new ArgumentNullException("baseIQueryable");

        this.Provider = baseIQueryable.Provider;            
        this.Expression = baseIQueryable.Expression;

        this.DataContext = context;
   }

   public IEnumerator<T> GetEnumerator()
   {
       var enumerator = Provider.Execute<IEnumerable<T>>(Expression);
       foreach( var item in enumerator )
       {
           item.DataContext = this.DataContext ?? new TContext();
           yield return item;
       }
   }

   IEnumerator IEnumerable.GetEnumerator()
   {
       var enumerator = Provider.Execute<IEnumerable>(Expression);
       foreach( var item in enumerator )
       {
           ((IEntityBase<TContext>)item).DataContext = this.DataContext;
           yield return item;
       }
   } 

   //
   //Other implementations...
   //
   public SampleDataContext DataContext{ get; set; } 
} 

public partial class Product: IEntityBase
{
    public MyQueryable<Product> GetProducts() 
    { 
        var result = from p in context.Products 
                     where {Some Conditions 1} 
                     select p; 
        return new MyQueryable<typeof(Product), DataContext>(this.DataContext, result);
    }         
}

Ответы [ 2 ]

1 голос
/ 28 января 2009

Я не могу придумать простой способ сделать это. Вы не можете ввести в середину конвейера LINQ-to-SQL, не нарушая компоновку. Самый простой способ сделать это - последний шаг через LINQ-to-Objects:

public IEnumerable<Product> GetProducts() 
{ 
    IQueryable<Product> result = from p in context.Products 
                                  where {Some Conditions 1} 
                                  select p; 
    return result.AsEnumerable().Select( x => {
       x.SomeProp = context;
       return x;
    });
}

Но учтите, что это нарушает возможность компоновки - все, что ниже, это LINQ-to-Objects.

Так как у вас есть общий базовый класс / интерфейс для ваших сущностей, это может быть альтернативно заключено в метод расширения для очень похожего поведения (но лучше повторного использования):

   return result.AssociateWith(context);

с чем-то вроде:

public static IEnumerable<T> AssociateWith<T>(
        this IEnumerable<T> source,
        DataContext context)
    where T : IEntityBase
{
    foreach(T item in source)
    {
        item.DataContext = context;
        yield return item;
    }
}
0 голосов
/ 04 февраля 2009

В Wayward Weblog есть отличное учебное пособие для работы с IQueryable и связанным * инструментарием . Похоже, вы нашли решение, которое работает для вас.

Я не уверен, почему вы захотите сделать это, если вы не пытаетесь заставить сущности LINQ to SQL следовать шаблону Active Record . Если это ваша цель, я бы порекомендовал добавить статические методы GetById, Query, Insert, Update и Delete на вашу базу и использовать методы расширения для добавления методов к объектам. Внутри каждого вы можете создать новый контекст данных и присоединить объект к этому контексту, когда будете готовы выполнить действие.

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

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