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

发布于 2025-01-01 10:05:57 字数 1329 浏览 1 评论 0原文

我正在尝试公开一个可用于 OData 服务的模型。我当前采用的方法大致如下:

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

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

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

public class MyDataService : DataService<MyEntities>
{
    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 吗?

谢谢。

I'm trying to expose a model to be available for OData services. The approach I'm currently taking is along the lines of:

1) Defining a class in the model to expose IQueryable collections such as:

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

2) Set up a WCF DataService with the queryable collection class such as:

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

I'm running into 3 issues and/or limitations with this approach:

1) I'm unable to add any derived class collections to the IQueryable lists.

2) I must apply the IgnoreProperties attribute to hide any members that are derived from a base type.

3) I'm unable to prevent unwanted entities from being accessed by the OData service and causing errors. For example, I only want BLL layer objects to be exposed, but it seems like the model is being reflected far beyond the members of the classes I added to the queryable list, and picking up all the DAL classes, causing errors being undefined and also having the same name as the BLL classes. There are no links to DAL classes from BLL class members. At the very least, I would like to have these classes ignored altogether.

Any pointers on how to address any of these issues would be greatly appreciated. Should I be doing a different approach on this? For example, should I implement IQueryable directly in my model collections?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我早已燃尽 2025-01-08 10:05:57

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

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

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

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

The reflection provider which you're using is designed to walk all public types/properties. So the #3 and probably even #2 (which I don't fully understand what's the problem) are by design because of that.

#1 is also by design but for a different reason - the reflection provider can only expose one entity set for each type hierarchy. It doesn't support so called "MEST" (Multiple Entity Sets per Type), because it would not know which one to pick. It needs a 1 to 1 mapping between entity types and entity sets.

The reflection provider is meant for simple services which are "Easy" to setup. It's definitely not designed for customizations.

If you want greater control, then you need custom provider, which can be either implemented directly (if it's based on existing CLR classes it's not that hard), or through some library, like the one suggested in the comments above.

颜漓半夏 2025-01-08 10:05:57

反射提供程序并非旨在处理具有大量继承和其他依赖性的丰富数据模型。我最终构建了一个自定义提供程序,可以根据 Alex James 在 创建数据服务提供者

下面提供了包含 3 个 CLR 类的示例实现:ResidentialCustomerCustomerUserResidentialCustomer 扩展了 CustomerCustomer 有一个 User 列表,并且 User 有返回到客户的引用。

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<string, ResourceType> resourceTypes = new Dictionary<string, ResourceType>();
    private Dictionary<string, ResourceSet> resourceSets = new Dictionary<string, ResourceSet>();
    private List<ResourceAssociationSet> _associationSets = new List<ResourceAssociationSet>(); 

    public string ContainerName
    {
        get { return "MyDataContext"; }
    }

     public string ContainerNamespace
     {
         get { return "MyNamespace"; }
     }

    public IEnumerable<ResourceSet> ResourceSets
    {
         get { return this.resourceSets.Values; }
    }

    public IEnumerable<ServiceOperation> ServiceOperations
    {
        get { yield break; }
    }

    public IEnumerable<ResourceType> 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<ResourceType> GetDerivedTypes(ResourceType resourceType)
    {
        List<ResourceType> derivedResourceTypes = new List<ResourceType>();
        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<T> : 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<KeyValuePair<string, object>> 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;
    }
}

用于实现 < code>IDataServiceUpdateProvider 例如:

public class ODataServiceUpdateProvider<T> : IDataServiceUpdateProvider where T : IODataContext
{
    private IDataServiceMetadataProvider _metadata;
    private ODataServiceQueryProvider<T> _query;
    private List<Action> _actions;

    public T GetContext()
    {
        return ((T)_query.CurrentDataSource);
    }

    public void SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable<KeyValuePair<string, object>> 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<T> query)
     {
         _metadata = metadata;
         _query = query;
         _actions = new List<Action>();
    }
}

实现 IServiceProvider 的类,例如:

public class ODataService<T> : DataService<T>, IServiceProvider where T : IODataContext
{
    private ODataServiceMetadataProvider _metadata;
    private ODataServiceQueryProvider<T> _query;
    private ODataServiceUpdateProvider<T> _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<T> GetQueryProvider(ODataServiceMetadataProvider metadata)
    {
        return new ODataServiceQueryProvider<T>(metadata);
    }

    public ODataServiceUpdateProvider<T> GetUpdateProvider(ODataServiceMetadataProvider metadata, ODataServiceQueryProvider<T> query)
    {
        return new ODataServiceUpdateProvider<T>(metadata, query);
    }

    public ODataService()
    {
        _metadata = GetMetadataProvider(typeof(T));
        _query = GetQueryProvider(_metadata);
        _updater = GetUpdateProvider(_metadata, _query);
    }
}

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

public partial class MyDataContext: IODataContext
{
    private List<Customer> _customers = null;
    public List<Customer> Customers
    {
        get
        {
            if (_customers == null)
            {
                _customers = DataManager.GetCustomers);
            }
            return _customers;
        }
    }

    private List<ResidentialCustomer> _residentialCustomers = null;
    public List<ResidentialCustomer> ResidentialCustomers
    {
        get
        {
            if (_residentialCustomers == null)
            {
                _residentialCustomers = DataManager.GetResidentialCustomers();
            }
            return _residentialCustomers;
        }
    }

    private List<User> _users = null;
    public List<User> 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<MyDataContext>
{
    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; 
    }
}

大量连接,但一旦掌握了窍门就非常简单。

The reflection provider is not designed to handle rich data models with a fair amount of inheritance and other dependences. I ended up building a custom provider that could handle queries, updates, inheritance, and relationships based on Alex James' excellent blog post on Creating a Data Service Provider.

An example implementation with 3 CLR classes: ResidentialCustomer, Customer, and User is provided below. ResidentialCustomer extends Customer, Customer has a list of Users, and User has a reference back to Customer.

An interface for DataContext classes such as:

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

A class to implement IDataServiceMetadataProvider such as:

public class ODataServiceMetadataProvider : IDataServiceMetadataProvider
{
    private Dictionary<string, ResourceType> resourceTypes = new Dictionary<string, ResourceType>();
    private Dictionary<string, ResourceSet> resourceSets = new Dictionary<string, ResourceSet>();
    private List<ResourceAssociationSet> _associationSets = new List<ResourceAssociationSet>(); 

    public string ContainerName
    {
        get { return "MyDataContext"; }
    }

     public string ContainerNamespace
     {
         get { return "MyNamespace"; }
     }

    public IEnumerable<ResourceSet> ResourceSets
    {
         get { return this.resourceSets.Values; }
    }

    public IEnumerable<ServiceOperation> ServiceOperations
    {
        get { yield break; }
    }

    public IEnumerable<ResourceType> 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<ResourceType> GetDerivedTypes(ResourceType resourceType)
    {
        List<ResourceType> derivedResourceTypes = new List<ResourceType>();
        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() { }
}

A class to implement IDataServiceQueryProvider such as:

public class ODataServiceQueryProvider<T> : 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<KeyValuePair<string, object>> 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;
    }
}

A class to implement IDataServiceUpdateProvider such as:

public class ODataServiceUpdateProvider<T> : IDataServiceUpdateProvider where T : IODataContext
{
    private IDataServiceMetadataProvider _metadata;
    private ODataServiceQueryProvider<T> _query;
    private List<Action> _actions;

    public T GetContext()
    {
        return ((T)_query.CurrentDataSource);
    }

    public void SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable<KeyValuePair<string, object>> 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<T> query)
     {
         _metadata = metadata;
         _query = query;
         _actions = new List<Action>();
    }
}

A class to implement IServiceProvider such as:

public class ODataService<T> : DataService<T>, IServiceProvider where T : IODataContext
{
    private ODataServiceMetadataProvider _metadata;
    private ODataServiceQueryProvider<T> _query;
    private ODataServiceUpdateProvider<T> _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<T> GetQueryProvider(ODataServiceMetadataProvider metadata)
    {
        return new ODataServiceQueryProvider<T>(metadata);
    }

    public ODataServiceUpdateProvider<T> GetUpdateProvider(ODataServiceMetadataProvider metadata, ODataServiceQueryProvider<T> query)
    {
        return new ODataServiceUpdateProvider<T>(metadata, query);
    }

    public ODataService()
    {
        _metadata = GetMetadataProvider(typeof(T));
        _query = GetQueryProvider(_metadata);
        _updater = GetUpdateProvider(_metadata, _query);
    }
}

The DataContext class holds the CLR collections and wires up the service operations such as:

public partial class MyDataContext: IODataContext
{
    private List<Customer> _customers = null;
    public List<Customer> Customers
    {
        get
        {
            if (_customers == null)
            {
                _customers = DataManager.GetCustomers);
            }
            return _customers;
        }
    }

    private List<ResidentialCustomer> _residentialCustomers = null;
    public List<ResidentialCustomer> ResidentialCustomers
    {
        get
        {
            if (_residentialCustomers == null)
            {
                _residentialCustomers = DataManager.GetResidentialCustomers();
            }
            return _residentialCustomers;
        }
    }

    private List<User> _users = null;
    public List<User> 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();
    }
}

Then, create your data service using the custom data service class and your data context, such as:

public class MyDataService : ODataService<MyDataContext>
{
    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; 
    }
}

Lots of wiring up, but pretty straightforward once you've got the hang of it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文