如何公开具有OData服务的派生类和成员的多层模型?

我正在尝试公开一个可用于OData服务的模型。 我目前采取的方法是:

1)在模型中定义一个类以公开IQueryable集合,例如:

 public class MyEntities { public IQueryable Customers { get { return DataManager.GetCustomers().AsQueryable(); } } public IQueryable Users { get { return DataManager.GetUsers().AsQueryable(); } } } 

2)使用可查询的集合类设置WCF DataService ,例如:

 public class MyDataService : DataService { public static void InitializeService(DataServiceConfiguration config) { config.SetEntitySetAccessRule("Customers", EntitySetRights.All); config.SetEntitySetAccessRule("Users", EntitySetRights.All); config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2; } } 

我用这种方法遇到了3个问题和/或限制:

1)我无法将任何派生类集合添加到IQueryable列表中。

2)我必须应用IgnoreProperties属性来隐藏从基类型派生的任何成员。

3)我无法阻止OData服务访问不需要的实体并导致错误。 例如,我只希望暴露BLL层对象,但似乎模型的反映远远超出了我添加到可查询列表中的类的成员,并且拾取了所有DAL类,导致错误未定义,并且与BLL类同名。 BLL类成员没有指向DAL类的链接。 至少,我想完全忽略这些类。

任何有关如何解决这些问题的指示都将不胜感激。 我应该对此采取不同的方法吗? 例如,我应该直接在我的模型集合中实现IQueryable吗?

谢谢。

您正在使用的reflection提供程序旨在遍历所有公共类型/属性。 因此,#3甚至可能是#2(我不完全理解问题是什么)都是设计因此。

#1也是设计但由于不同的原因 – reflection提供者只能为每个类型层次结构公开一个实体集。 它不支持所谓的“MEST”(每种类型的多个实体集),因为它不知道要选择哪一个。 它需要实体类型和实体集之间的1对1映射。

reflection提供程序适用于“易于”设置的简单服务。 它绝对不是为自定义而设计的。

如果你想要更好的控制,那么你需要自定义提供程序,它可以直接实现(如果它基于现有的CLR类并不那么难),或者通过某些库,如上面评论中建议的那样。

reflection提供程序不是为处理具有大量inheritance和其他依赖性的丰富数据模型而设计的。 我最终构建了一个自定义提供程序,可以根据Alex James关于创建数据服务提供程序的优秀博客文章来处理查询,更新,inheritance和关系。

下面提供了3个CLR类的示例实现: ResidentialCustomerCustomerUserResidentialCustomer扩展了CustomerCustomer有一个User列表, User可以向Customer提供参考。

DataContext类的接口,例如:

 public interface IODataContext { IQueryable GetQueryable(ResourceSet set); object CreateResource(ResourceType resourceType); void AddResource(ResourceType resourceType, object resource); void DeleteResource(object resource); void SaveChanges(); } 

实现IDataServiceMetadataProvider的类,例如:

 public class ODataServiceMetadataProvider : IDataServiceMetadataProvider { private Dictionary resourceTypes = new Dictionary(); private Dictionary resourceSets = new Dictionary(); private List _associationSets = new List(); public string ContainerName { get { return "MyDataContext"; } } public string ContainerNamespace { get { return "MyNamespace"; } } public IEnumerable ResourceSets { get { return this.resourceSets.Values; } } public IEnumerable ServiceOperations { get { yield break; } } public IEnumerable Types { get { return this.resourceTypes.Values; } } public bool TryResolveResourceSet(string name, out ResourceSet resourceSet) { return resourceSets.TryGetValue(name, out resourceSet); } public bool TryResolveResourceType(string name, out ResourceType resourceType) { return resourceTypes.TryGetValue(name, out resourceType); } public bool TryResolveServiceOperation(string name, out ServiceOperation serviceOperation) { serviceOperation = null; return false; } public void AddResourceType(ResourceType type) { type.SetReadOnly(); resourceTypes.Add(type.FullName, type); } public void AddResourceSet(ResourceSet set) { set.SetReadOnly(); resourceSets.Add(set.Name, set); } public bool HasDerivedTypes(ResourceType resourceType) { if (resourceType.InstanceType == typeof(ResidentialCustomer)) { return true; } return false; } public IEnumerable GetDerivedTypes(ResourceType resourceType) { List derivedResourceTypes = new List(); if (resourceType.InstanceType == typeof(ResidentialCustomer)) { foreach (ResourceType resource in Types) { if (resource.InstanceType == typeof(Customer)) { derivedResourceTypes.Add(resource); } } } return derivedResourceTypes; } public void AddAssociationSet(ResourceAssociationSet associationSet) { _associationSets.Add(associationSet); } public ResourceAssociationSet GetResourceAssociationSet(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty) { return resourceProperty.CustomState as ResourceAssociationSet; } public ODataServiceMetadataProvider() { } } 

实现IDataServiceQueryProvider的类,例如:

 public class ODataServiceQueryProvider : IDataServiceQueryProvider where T : IODataContext { T _currentDataSource; IDataServiceMetadataProvider _metadata; public object CurrentDataSource { get { return _currentDataSource; } set { _currentDataSource = (T)value; } } public bool IsNullPropagationRequired { get { return true; } } public object GetOpenPropertyValue(object target, string propertyName) { throw new NotImplementedException(); } public IEnumerable> GetOpenPropertyValues(object target) { throw new NotImplementedException(); } public object GetPropertyValue(object target, ResourceProperty resourceProperty) { throw new NotImplementedException(); } public IQueryable GetQueryRootForResourceSet(ResourceSet resourceSet) { return _currentDataSource.GetQueryable(resourceSet); } public ResourceType GetResourceType(object target) { Type type = target.GetType(); return _metadata.Types.Single(t => t.InstanceType == type); } public object InvokeServiceOperation(ServiceOperation serviceOperation, object[] parameters) { throw new NotImplementedException(); } public ODataServiceQueryProvider(IDataServiceMetadataProvider metadata) { _metadata = metadata; } } 

实现IDataServiceUpdateProvider的类,例如:

 public class ODataServiceUpdateProvider : IDataServiceUpdateProvider where T : IODataContext { private IDataServiceMetadataProvider _metadata; private ODataServiceQueryProvider _query; private List _actions; public T GetContext() { return ((T)_query.CurrentDataSource); } public void SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable> concurrencyValues) { throw new NotImplementedException(); } public void SetReference(object targetResource, string propertyName, object propertyValue) { _actions.Add(() => ReallySetReference(targetResource, propertyName, propertyValue)); } public void ReallySetReference(object targetResource, string propertyName, object propertyValue) { targetResource.SetPropertyValue(propertyName, propertyValue); } public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded) { _actions.Add(() => ReallyAddReferenceToCollection(targetResource, propertyName, resourceToBeAdded)); } public void ReallyAddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded) { var collection = targetResource.GetPropertyValue(propertyName); if (collection is IList) { (collection as IList).Add(resourceToBeAdded); } } public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved) { _actions.Add(() => ReallyRemoveReferenceFromCollection(targetResource, propertyName, resourceToBeRemoved)); } public void ReallyRemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved) { var collection = targetResource.GetPropertyValue(propertyName); if (collection is IList) { (collection as IList).Remove(resourceToBeRemoved); } } public void ClearChanges() { _actions.Clear(); } public void SaveChanges() { foreach (var a in _actions) a(); GetContext().SaveChanges(); } public object CreateResource(string containerName, string fullTypeName) { ResourceType type = null; if (_metadata.TryResolveResourceType(fullTypeName, out type)) { var context = GetContext(); var resource = context.CreateResource(type); _actions.Add(() => context.AddResource(type, resource)); return resource; } throw new Exception(string.Format("Type {0} not found", fullTypeName)); } public void DeleteResource(object targetResource) { _actions.Add(() => GetContext().DeleteResource(targetResource)); } public object GetResource(IQueryable query, string fullTypeName) { var enumerator = query.GetEnumerator(); if (!enumerator.MoveNext()) throw new Exception("Resource not found"); var resource = enumerator.Current; if (enumerator.MoveNext()) throw new Exception("Resource not uniquely identified"); if (fullTypeName != null) { ResourceType type = null; if (!_metadata.TryResolveResourceType(fullTypeName, out type)) throw new Exception("ResourceType not found"); if (!type.InstanceType.IsAssignableFrom(resource.GetType())) throw new Exception("Unexpected resource type"); } return resource; } public object ResetResource(object resource) { _actions.Add(() => ReallyResetResource(resource)); return resource; } public void ReallyResetResource(object resource) { var clrType = resource.GetType(); ResourceType resourceType = _metadata.Types.Single(t => t.InstanceType == clrType); var resetTemplate = GetContext().CreateResource(resourceType); foreach (var prop in resourceType.Properties .Where(p => (p.Kind & ResourcePropertyKind.Key) != ResourcePropertyKind.Key)) { var clrProp = clrType.GetProperties().Single(p => p.Name == prop.Name); var defaultPropValue = clrProp.GetGetMethod().Invoke(resetTemplate, new object[] { }); clrProp.GetSetMethod().Invoke(resource, new object[] { defaultPropValue }); } } public object ResolveResource(object resource) { return resource; } public object GetValue(object targetResource, string propertyName) { var value = targetResource.GetType().GetProperties().Single(p => p.Name == propertyName).GetGetMethod().Invoke(targetResource, new object[] { }); return value; } public void SetValue(object targetResource, string propertyName, object propertyValue) { targetResource.GetType().GetProperties().Single(p => p.Name == propertyName).GetSetMethod().Invoke(targetResource, new[] { propertyValue }); } public ODataServiceUpdateProvider(IDataServiceMetadataProvider metadata, ODataServiceQueryProvider query) { _metadata = metadata; _query = query; _actions = new List(); } } 

实现IServiceProvider的类,例如:

 public class ODataService : DataService, IServiceProvider where T : IODataContext { private ODataServiceMetadataProvider _metadata; private ODataServiceQueryProvider _query; private ODataServiceUpdateProvider _updater; public object GetService(Type serviceType) { if (serviceType == typeof(IDataServiceMetadataProvider)) { return _metadata; } else if (serviceType == typeof(IDataServiceQueryProvider)) { return _query; } else if (serviceType == typeof(IDataServiceUpdateProvider)) { return _updater; } else { return null; } } public ODataServiceMetadataProvider GetMetadataProvider(Type dataSourceType) { ODataServiceMetadataProvider metadata = new ODataServiceMetadataProvider(); ResourceType customer = new ResourceType( typeof(Customer), ResourceTypeKind.EntityType, null, "MyNamespace", "Customer", false ); ResourceProperty customerCustomerID = new ResourceProperty( "CustomerID", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Guid)) ); customer.AddProperty(customerCustomerID); ResourceProperty customerCustomerName = new ResourceProperty( "CustomerName", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string)) ); customer.AddProperty(customerCustomerName); ResourceType residentialCustomer = new ResourceType( typeof(ResidentialCustomer), ResourceTypeKind.EntityType, customer, "MyNamespace", "ResidentialCustomer", false ); ResourceType user = new ResourceType( typeof(User), ResourceTypeKind.EntityType, null, "MyNamespace", "User", false ); ResourceProperty userUserID = new ResourceProperty( "UserID", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Guid)) ); user.AddProperty(userUserID); ResourceProperty userCustomerID = new ResourceProperty( "CustomerID", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Guid)) ); user.AddProperty(userCustomerID); ResourceProperty userEmailAddress = new ResourceProperty( "EmailAddress", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string)) ); user.AddProperty(userEmailAddress); var customerSet = new ResourceSet("Customers", customer); var residentialCustomerSet = new ResourceSet("ResidentialCustomers", residentialCustomer); var userSet = new ResourceSet("Users", user); var userCustomer = new ResourceProperty( "Customer", ResourcePropertyKind.ResourceReference, customer ); user.AddProperty(userCustomer); var customerUserList = new ResourceProperty( "UserList", ResourcePropertyKind.ResourceSetReference, user ); customer.AddProperty(customerUserList); metadata.AddResourceType(customer); metadata.AddResourceSet(customerSet); metadata.AddResourceType(residentialCustomer); metadata.AddResourceSet(residentialCustomerSet); metadata.AddResourceType(user); metadata.AddResourceSet(userSet); ResourceAssociationSet customerUserListSet = new ResourceAssociationSet( "CustomerUserList", new ResourceAssociationSetEnd( customerSet, customer, customerUserList ), new ResourceAssociationSetEnd( userSet, user, userCustomer ) ); customerUserList.CustomState = customerUserListSet; userCustomer.CustomState = customerUserListSet; metadata.AddAssociationSet(customerUserListSet); return metadata; } public ODataServiceQueryProvider GetQueryProvider(ODataServiceMetadataProvider metadata) { return new ODataServiceQueryProvider(metadata); } public ODataServiceUpdateProvider GetUpdateProvider(ODataServiceMetadataProvider metadata, ODataServiceQueryProvider query) { return new ODataServiceUpdateProvider(metadata, query); } public ODataService() { _metadata = GetMetadataProvider(typeof(T)); _query = GetQueryProvider(_metadata); _updater = GetUpdateProvider(_metadata, _query); } } 

DataContext类保存CLR集合并连接服务操作,例如:

 public partial class MyDataContext: IODataContext { private List _customers = null; public List Customers { get { if (_customers == null) { _customers = DataManager.GetCustomers); } return _customers; } } private List _residentialCustomers = null; public List ResidentialCustomers { get { if (_residentialCustomers == null) { _residentialCustomers = DataManager.GetResidentialCustomers(); } return _residentialCustomers; } } private List _users = null; public List Users { get { if (_users == null) { _users = DataManager.GetUsers(); } return _users; } } public IQueryable GetQueryable(ResourceSet set) { if (set.Name == "Customers") return Customers.AsQueryable(); if (set.Name == "ResidentialCustomers") return ResidentialCustomers.AsQueryable(); if (set.Name == "Users") return Users.AsQueryable(); throw new NotSupportedException(string.Format("{0} not found", set.Name)); } public object CreateResource(ResourceType resourceType) { if (resourceType.InstanceType == typeof(Customer)) { return new Customer(); } if (resourceType.InstanceType == typeof(ResidentialCustomer)) { return new ResidentialCustomer(); } if (resourceType.InstanceType == typeof(User)) { return new User(); } throw new NotSupportedException(string.Format("{0} not found for creating.", resourceType.FullName)); } public void AddResource(ResourceType resourceType, object resource) { if (resourceType.InstanceType == typeof(Customer)) { Customer i = resource as Customer; if (i != null) { Customers.Add(i); return; } } if (resourceType.InstanceType == typeof(ResidentialCustomer)) { ResidentialCustomeri = resource as ResidentialCustomer; if (i != null) { ResidentialCustomers.Add(i); return; } } if (resourceType.InstanceType == typeof(User)) { Useri = resource as User; if (i != null) { Users.Add(i); return; } } throw new NotSupportedException(string.Format("{0} not found for adding.", resourceType.FullName)); } public void DeleteResource(object resource) { if (resource.GetType() == typeof(Customer)) { Customers.Remove(resource as Customer); return; } if (resource.GetType() == typeof(ResidentialCustomer)) { ResidentialCustomers.Remove(resource as ResidentialCustomer); return; } if (resource.GetType() == typeof(User)) { Users.Remove(resource as User); return; } throw new NotSupportedException(string.Format("{0} not found for deletion.", resource.GetType().FullName)); } public void SaveChanges() { foreach (var item in Customers.Where(i => i.IsModified == true)) item.Save(); foreach (var item in ResidentialCustomers.Where(i => i.IsModified == true)) item.Save(); foreach (var item in Users.Where(i => i.IsModified == true)) item.Save(); } } 

然后,使用自定义数据服务类和数据上下文创建数据服务,例如:

 public class MyDataService : ODataService { public static void InitializeService(DataServiceConfiguration config) { config.SetEntitySetAccessRule("Customers", EntitySetRights.All); config.SetEntitySetAccessRule("ResidentialCustomers", EntitySetRights.All); config.SetEntitySetAccessRule("Users", EntitySetRights.All); config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2; config.DataServiceBehavior.AcceptProjectionRequests = true; } } 

很多接线,但一旦你掌握了它,就非常简单。