2009-01-28 39 views
3

我想定製我的應用程序的實體,使他們有一個屬性引用加載它們在DataContext。自定義的IQueryable <T>

我認爲最好的辦法就是以某種方式創建一個實現IQueryable的一類,並設置在其GetEnumerator方法實體DataContext屬性。

我的問題是,我該如何使用使用LINQ的我在執行的IQueryable對於SQL,這樣我就不必執行他們自己的供應商和表達?

BTW:對於我的情況下,有另一種方式?

下面的代碼看看:

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); 
    }   
} 

回答

1

我想不出一個簡單的方法來做到這一點。您不能注入到LINQ-to-SQL管道的中間而不破壞可組合性。要做到這一點,最簡單的辦法是經由LINQ到對象最後一步:

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到對象。

既然你有你的實體的共同基類/接口,這可以替代性地包裹在一個擴展方法非常類似的行爲(但更好的再利用):

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

看來,有沒有辦法設置屬性不會破壞組合性。我對嗎?如果是的話EF呢?我可以用EF做這個嗎? – mrtaikandi 2009-01-28 11:49:23

0

任性的博客有很大tutorial for working with IQueryable和相關toolkit。看起來你找到了適合你的解決方案。

我不知道你爲什麼會想這樣做,除非你試圖讓LINQ to SQL的實體遵循Active Record模式。如果這是您的目標,我會建議您在您的基礎上添加靜態GetById,Query,Insert,Update和Delete方法,並使用擴展方法將這些方法添加到實體中。在每個內部,您可以創建新的數據上下文,並在準備好執行操作時將該實體附加到該上下文。

DataContext跟在unit of work pattern之後,因此在完成您執行的操作時應該是kept alive only a short while