3

我正在尝试自定义应用程序的实体,以使它们具有引用加载它们的 DataContext 的属性。

我认为最好的方法是以某种方式创建一个实现 IQueryable 的类并在其 GetEnumerator 方法中设置实体 datacontext 属性。

我的问题是,如何在我的 IQueryable 实现中使用 Linq to SQL 使用的提供程序和表达式,这样我就不必自己实现它们了?

顺便说一句:对于我的场景,还有其他方法吗?

看看下面的代码:

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

更新

我自己找到了答案。这是显示我如何做到这一点的示例代码。

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

2 回答 2

1

我想不出一个简单的方法来做到这一点。您不能在不破坏可组合性的情况下注入 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;
    }
}
于 2009-01-28T09:08:19.327 回答
0

Wayward Weblog 有一个很好的使用 IQueryable和相关工具包的教程。看起来您找到了适合您的解决方案。

我不确定你为什么要这样做,除非你试图让 LINQ to SQL 实体遵循Active Record模式。如果这是您的目标,我建议您在基础上添加静态 GetById、Query、Insert、Update 和 Delete 方法,并使用扩展方法将这些方法添加到实体中。在每个内部,您可以创建一个新的数据上下文,并在您准备好执行操作时将实体附加到该上下文。

DataContext 遵循工作单元模式,因此,当您完成您执行的操作时,它应该只在短时间内保持活动状态。

于 2009-02-04T14:45:23.957 回答