我正在尝试自定义应用程序的实体,以使它们具有引用加载它们的DataContext的属性。
我认为最好的方法是以某种方式创建一个实现IQueryable的类,并在其GetEnumerator方法中设置实体datacontext属性。
我的问题是,如何在我的IQueryable实现中使用Linq to SQL使用的Provider和Expression,以便我自己不必实现它们?
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; };
}
更新
我自己找到了答案。这是示例代码,用于说明我是如何做到的。
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);
}
}
答案 0 :(得分: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;
}
}
答案 1 :(得分:0)
Wayward Weblog有一个很棒的tutorial for working with IQueryable和一个相关的toolkit。看起来您找到了适合您的解决方案。
除非您尝试使LINQ to SQL实体遵循Active Record模式,否则我不确定您为什么要这样做。如果这是你的目标,我建议你在你的基础上添加静态GetById,查询,插入,更新和删除方法,并使用扩展方法将方法添加到实体。在每个内部,您可以创建新的数据上下文,并在准备好执行操作时将实体附加到该上下文。
DataContext遵循unit of work pattern,因此,当您完成所执行的操作时,应该是kept alive only a short while。