2009-01-28 21 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