我正在开发一个Silverlight应用程序从WCF数据服务中提取数据,但是与服务的通信位于外观之后。我这样做是为了在没有实现服务器的情况下促进SL应用程序的开发,因此SL应用程序可以使用来自txt文件的数据而不知道其中的差异。
下面的接口构成了外观,具体类是它们在外观后面的实现。
public interface IDataContext
{
IEntityQuery<IRootEntity> Roots { get; }
IEntityQuery<IBranchEntity> Branches{ get; }
IEntityQuery<ILeafEntity> Leaves { get; }
}
public interface IEntityQuery<TEntity> : IQueryable<TModel>
{
IAsyncResult BeginExecute(AsyncCallback callback, object state);
IEnumerable<TModel> EndExecute(IAsyncResult asyncResult);
IEntityQuery<TModel> Where(Expression<Func<TModel, bool>> predicate);
}
public interface IEntityCollection<TEntity> : INotifyCollectionChanged,
INotifyPropertyChanged, IQueryable<TModel>
{
}
public interface IRootEntity
{
int Id { get; set; }
string Name { get; set; }
IModelCollection<IBranchEntity> Branches { get; set; }
}
public interface IBranchEntity
{
int Id { get; set; }
string Name { get; set; }
IRootEntity Root { get; set; }
IModelCollection<ILeafEntity> Leaves { get; set; }
}
public interface ILeafEntity
{
int Id { get; set; }
string Name { get; set; }
IBranchEntity Branch { get; set; }
}
// Partially implemented by me and partially by Visual Studio when the Service
// Reference was added.
public partial class Container : IDataContext
{
IEntityQuery<IRootEntity> IDataContext.Roots
{
get
{
return new ModelQuery<IRootEntity, RootEntity>(this.Roots);
}
}
IEntityQuery<IBranchEntity> IDataContext.Branches
{
get
{
return new ModelQuery<IBranchEntity, BranchEntity>(this.Roots);
}
}
IEntityQuery<ILeafEntity> IDataContext.Leaves
{
get
{
return new ModelQuery<ILeafEntity, LeafEntity>(this.Leaves);
}
}
}
public class EntityQuery<TFacade, TConcrete>
where TConcrete : class, TFacade
{
private DataServiceQuery<TConcrete> _dsq;
public ModelQuery(DataServiceQuery<TConcrete> dsq)
{
_dsq = dsq;
}
public IAsyncResult BeginExecute(AsyncCallback callback, object state)
{
return _dsq.BeginExecute(callback, state);
}
public IEnumerable<TFacade> EndExecute(IAsyncResult asyncResult)
{
return _dsq.EndExecute(asyncResult).AsEnumerable() as IEnumerable<TFacade>;
}
public IModelQuery<TFacade> Where(Expression<Func<TFacade, bool>> predicate)
{
Expression<Func<TConcrete, bool>> concretePredicate = Expression.Lambda<Func<TConcrete, bool>>(predicate.Body, predicate.Parameters);
return new ModelQuery<TFacade, TConcrete>(_dsq.Where(concretePredicate) as DataServiceQuery<TConcrete>);
}
// IQueryable implementation ...
}
public class EntityCollection<TFacade, TConcrete> : IEntityCollection<TFacade>
where TConcrete : class, TFacade
{
public EntityCollection(ObservableCollection<TConcrete> innerCollection)
{
this.InnerCollection = innerCollection;
this.InnerCollection.CollectionChanged += InnerCollection_CollectionChanged;
}
internal ObservableCollection<TConcrete> InnerCollection { get; private set; }
private void InnerCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
this.OnCollectionChanged(e);
}
// IQueryable<TFacade> implementation ...
}
// Partially implemented by me and partially by Visual Studio when the Service
// Reference was added.
public partial class RootEntity : IRootEntity
{
IList<IBranchEntity> IRootEntity.Branches
{
get { return this.Branches; }
set { this.Branches = value as IList<IBranchEntity>; }
}
}
// Partially implemented by me and partially by Visual Studio when the Service
// Reference was added.
public partial class BranchEntity : IBranchEntity
{
IRootEntity IBranchEntity.Root
{
get { return this.Root; }
set { this.Root = value as RootEntity; }
}
IList<ILeafEntity> IBranchEntity.Leaves
{
get { return this.Leaves; }
set { this.Leaves = value as IList<LeafEntity>; }
}
}
// Partially implemented by me and partially by Visual Studio when the Service
// Reference was added.
public partial class LeafEntity : ILeafEntity
{
IRootEntity ILeafEntity.Root
{
get { return this.Root; }
set { this.Root = value as RootEntity; }
}
}
EntityQuery
和EntityCollection
类对于维护外观的抽象是必要的。没有它们,SL应用程序必须知道DataServiceQuery
和DataServiceCollection
。
我遇到的问题是将SL应用程序开发人员针对Facade编写的LINQ查询转换为LINQ查询,客户端WCF数据服务代理可以将其转换为OData URL。
我已经能够运行一些简单的查询,但更复杂的查询会开始抛出异常。目前给我一个问题的查询如下:
IEntityQuery<IRootEntity> query = this.Context.Roots
.Where(r => r.Branches.Any(b=> b.Leaves.Any(l => l.Name == "Find Me")));
IRootEntity result = Task.Factory.StartNew(() => query.BeginExecute(null, null))
.ContinueWith(t => query.EndExecute(t.Result))
.Result
.Single();
我得到一个NotSupportedException
声明“'Any'方法的source参数必须是导航或集合属性。”我很确定这是因为Any()
是在ModelCollection<T1,T2>
而不是DataServiceCollection<T>
上调用的,因为它是InnerCollection,但我不知道该怎么办。
答案 0 :(得分:1)
问题很长(虽然问题很好),但答案很短。您无法公开IQueryable
并期望其leaky abstractions不会伤害您。
最后,界面由DataServiceQuery<T>
支持。有一长串不支持的LINQ方法:LINQ Considerations (WCF Data Services): see Unsupported LINQ Methods。除此之外,DataServiceQuery
具有有用的实例方法,但不能被您的界面利用,例如Expand
。
我担心这需要对您的架构进行重大改革:公开接受规范的方法并将这些方法转换为支持的linq查询在幕后。