自定义的IQueryable< T> [英] Customizing IQueryable<T>

查看:129
本文介绍了自定义的IQueryable< T>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想定制我的应用程序的实体,使他们有一个属性引用该加载它们的DataContext的。

我觉得最好的办法就是以某种方式创建一个实现IQueryable的一类,并设置实体的DataContext在它的GetEnumerator方法属性。

我的问题是,我该如何使用所用的LINQ提供程序和Ex pression在我执行的IQueryable到SQL,这样我就不必执行他们自己?

BTW:对于我的情况下,有另一种方式

看看下面code:

 公共部分类产品:IEntityBase
{

    公共产品()
    {
        _DataContext =新SampleDataContext();
    }

    专用长_id;
    [列(存储=_ ID,自动同步= AutoSync.OnInsert,的DbType =BigInt有NOT NULL认同,IsPrimaryKey = TRUE,IsDbGenerated =真)
    众长标识
    {
         {返回_id; }
         集合{_id = value的; }
    }

    私人字符串_Name;
    [列(存储=_姓名,的DbType =为nvarchar(max)NOT NULL,CanBeNull = FALSE
    公共字符串名称
    {
        {返回_Name; }
        集合{_Name =价值; }
    }

    私人SampleDataContext _DataContext;

    //这是属性扩展产品类以及正在返回这个类应设置
    //通过IQueryable的< T> .GetEnumerator()
    公共SampleDataContext的DataContext
    {
        {返回_Name; }
        集合{_Name =价值; }
    }

    公共MyQueryable<产品>的GetProducts()
    {
        MyQueryable<产品>结果=从p在context.Products
                                      其中{一些条件1}
                                      选择磷;
        result.DataContext = _DataContext;
        返回结果;
    }

    公共无效的someMethod()
    {
        //这个查询将不会实际设置DataCotnext属性。
        //而生成的sql查询是这样的:
        // SELECT * FROM产品WHERE {若干条件1}和{若干条件2}
        变种产品=的GetProducts(),其中({一些条件2})。

        //现在到GetEnumerator()被调用的产品DataContext属性
        //将被设置。
        的foreach(在产品VAR项)
        {
            item.Name =测试名称;
            item.DataContext.SubmitChanges();
        }
    }
}

公共MyQueryable< T>:IQueryable的< T>
    其中T:类,IEntityBase
{
    //
    // IQueryable的执行情况这是我的问题
   //

   公众的IEnumerator< T>的GetEnumerator()
   {
       的foreach()(VAR的Provider.GetEnumerator&LT项目; T&GT)
       {
            item.DataContext = this.DataContext;
            产生收益的项目;
       }
   }

   公共SampleDataContext的DataContext {获得;组; }
}

公共接口IEntityBase
{
    SampleDataContext的DataContext {获得;组; };
}
 

更新

我发现自己的答案。这是示例code,以显示我是如何做到这一点。

 公共MyQueryable< T,TContext计算值:IQueryable的< T>
    其中T:类,IEntityBase
    其中,TContext:DataContext的,新的()
{

   公共MyQueryable< T>(TContext背景下,IQueryable的< T> baseIQueryable)
   {
        如果(baseIQueryable == NULL)
            抛出新ArgumentNullException(baseIQueryable);

        this.Provider = baseIQueryable.Provider;
        this.Ex pression = baseIQueryable.Ex pression;

        this.DataContext =背景;
   }

   公众的IEnumerator< T>的GetEnumerator()
   {
       VAR枚举= Provider.Execute< IEnumerable的< T>>(出pression);
       的foreach(在枚举VAR项)
       {
           item.DataContext = this.DataContext?新TContext();
           产生收益的项目;
       }
   }

   IEnumerator的IEnumerable.GetEnumerator()
   {
       VAR枚举= Provider.Execute< IEnumerable的>(出pression);
       的foreach(在枚举VAR项)
       {
           ((IEntityBase< TContext>)项).DataContext = this.DataContext;
           产生收益的项目;
       }
   }

   //
   //其他实现...
   //
   公共SampleDataContext的DataContext {获得;组; }
}

公共部分类产品:IEntityBase
{
    公共MyQueryable<产品>的GetProducts()
    {
        VAR的结果=从p在context.Products
                     其中{一些条件1}
                     选择磷;
        返回新MyQueryable< typeof运算(产品)的DataContext>(this.DataContext,结果);
    }
}
 

解决方案

我想不出一个简单的方法来做到这一点。你不能注入到LINQ到SQL管道的中间不破坏组合性。要做到这一点,最简单的方法是为通过LINQ到对象的最后一个步骤:

 公开的IEnumerable<产品>的GetProducts()
{
    IQueryable的<产品>结果=从p在context.Products
                                  其中{一些条件1}
                                  选择磷;
    返回result.AsEnumerable()选择(X => {
       x.SomeProp =背景;
       返回X;
    });
}
 

但是请注意,这将中断组合性 - 所有下游LINQ到对象

既然你有一个共同的基类/接口,为您的实体,这可以备选地裹在一个扩展方法很类似的行为(但更好地重复使用):

 返回result.AssociateWith(上下文);
 

的东西,如:

 公共静态的IEnumerable< T> AssociateWith< T>(
        这IEnumerable的< T>资源,
        DataContext的情况下)
    其中T:IEntityBase
{
    的foreach(源牛逼项)
    {
        item.DataContext =背景;
        产生收益的项目;
    }
}
 

I'm trying to customize entities of my application to make them have a property referencing the DataContext that loaded them.

I think the best way is to somehow create a class that implements the IQueryable and set the entity datacontext property in its GetEnumerator method.

My question is, how can I use the Provider and Expression used by Linq to SQL in my implementation of IQueryable so that I don't have to implement them myself?

BTW: For my scenario, is there another way?

Take a look at the following code:

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

I found the answer myself. Here it is the sample code to show how I did that.

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

解决方案

I can't think of an easy way to do that. You can't inject into the middle of the LINQ-to-SQL pipeline without breaking composability. The easiest way to do this would be as a last step via 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;
    });
}

But note that this breaks composability - everything downstream is LINQ-to-Objects.

Since you have a common base-class / interface for your entities, this could alternatively be wrapped in an extension method for very similar behaviour (but better re-use):

   return result.AssociateWith(context);

with something like:

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

这篇关于自定义的IQueryable&LT; T&GT;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆