zoukankan      html  css  js  c++  java
  • 自定义Data Service Providers — (4)IDataServiceMetadataProvider元数据提供者

    完整教程目录请参见:《自定义Data Service Providers — 简介

    在这个章节我们将了解如何实现IDataServiceMetadataProvider接口。

    IDataServiceMetadataProvider对外提供你的数据源有哪些资源类型(ResourceType)和资源集(ResourceSet)

    那么你的数据源可能有哪些资源类型呢?

    l         他可能是完全静态的,例如,在你的软件中或模型中,资源类型在软件设计时就已经是确定的,像TwriterStackoverflow这样的具体项目;

    l         他可能是强类型的,例如你可能有个强类型类(表示你的数据源,使用T表示),通过他来反射获取有哪些属性,并在创建资源集时返回IQueryable<T>这样的结果,以及通过类型T来找到有多少资源类型(ResourceType)Entity Framework就是这样方式。

    l         他可能完全是动态的,例如你写了一个通用的DSP,只需要提供Database信息就可以对外提供元数据,在这种情况下,软件设计之初完全不知道最终运行时的类型,一切都是动态的。

    灵活性还是很好的。

    不论哪种方式,第一步你还是要建立一个实现IDataServiceMetadataProvider接口的类。

    public class DSPMetadata: IDataServiceMetadataProvider

    这个接口包含6个方法和5个属性,听起来有点复杂,但是在我的第一个例子里,我只花了不到半个小时就搞定了。

    public abstract class DSPDataService<T>: DataService<T>

       IDataServiceMetadataProvider _metadata;

     

       public DSPMetadata()

       {

          _metadata = GetMetadataProvider(typeof(T));

       }

     

       public object GetService(Type serviceType)

       {

          if (serviceType == typeof(IDataServiceMetadataProvider))

          {

             return _metadata;

          }

          …    

       } 

     

       public abstract IDataServiceMetadataProvider GetMetadataProvider(Type dataSourceType);

    }

    在这段代码中,你可以看见我定义了一个抽象方法用来获取元数据,你必须在派生类中实现它。

    在实现这个接口之前,你还得了解……

    创建元数据

    也许你已经注意到这个接口上包含这些概念:ResourceTypesResourcePropertiesServiceOperationsResourceSetsResourceAssociationSets

    这些类公开定义在System.Data.Services组装件下,这样你就能够使用这些类来描述你的数据模型,注意这些类仅仅用来描述你的模型信息。

    试想一下你有一个CLR类:

    public class Product

    {

    public int ProdKey {get;set;}

    public string Name {get;set;}

    public Decimal Price {get;set;}

    public Decimal Cost {get;set;}

    }

    现在你需要为这个Product类创建一个ResourceType来描述它,你该怎样做呢

    var productType = new ResourceType(

        typeof(Product), // 资源在最终实例化时的CLR类型

        ResourceTypeKind.EntityType, // 例如是实体、复杂类型等

        null, // 此类型的基类

        "Namespace", // 命名空间

        "Product", // 名称

        false // 是否是抽象类型

    );

    var prodKey = new ResourceProperty(

       "ProdKey",

       ResourcePropertyKind.Key |

       ResourcePropertyKind.Primitive,

       ResourceType.GetPrimitiveResourceType(typeof(int))

    );

    var prodName = new ResourceProperty(

       "Name", 

       ResourcePropertyKind.Primitive,

       ResourceType.GetPrimitiveResourceType(typeof(string))

    );

    var prodPrice = new ResourceProperty(

       "Price", 

       ResourcePropertyKind.Primitive,

       ResourceType.GetPrimitiveResourceType(typeof(Decimal))

    );

    productType.AddProperty(prodKey);

    productType.AddProperty(prodName);

    productType.AddProperty(prodPrice);

    注意:我们并没有提供Cost(成本)属性,因为我们假设他是敏感信息。

    下一步,我们将使用这个资源类型来创建一个叫Products的资源集,非常简单:

    var productsSet = new ResourceSet("Products", productType);

    正如你所看到的,构建一个元数据并不难。

    冻结你的元数据

    最后一个步骤就是要冻结你的元数据信息:

    productsSet.SetReadonly();

    productType.SetReadonly();

    这句话的意思是:不可以修改元数据了,至少在当前请求下不可以。

    注意:Data Services在每次请求前,都将查询IDataServiceMetadataProvider实现程序其元数据是否已冻结,如果未冻结,那么每次请求都将要求重建元数据。

    通过IDataServiceMetadataProvider暴露元数据

    一旦建立好元数据,你就需要暴露这些元数据。

    如果你不打算支持继承、关系和服务操作的话,那么实现就比较简单,就像这样:

    public class DSPMetadataProvider

        private Dictionary<string, ResourceType> _resourceTypes 

           = new Dictionary<string, ResourceType>();

        private Dictionary<string, ResourceSet> _resourceSets

           = new Dictionary<string, ResourceSet>();

     

        public DSPMetadataProvider(){}

     

        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 string ContainerName

        {

            get { return "Container"; }

        } 

     

        public string ContainerNamespace

        {

            get { return "Namespace"; }

        }

     

        public IEnumerable<ResourceType> GetDerivedTypes(

            ResourceType resourceType

        )

        {

            //这里我们暂不实现继承

            yield break;

        } 

     

        public ResourceAssociationSet GetResourceAssociationSet(

            ResourceSet resourceSet,

            ResourceType resourceType,

            ResourceProperty resourceProperty)

        {

            throw new NotImplementedException("不支持关系");

        } 

     

        public bool HasDerivedTypes(ResourceType resourceType)

        {

            // 这里我们暂不实现继承

            return false;

        }

     

        public IEnumerable<ResourceSet> ResourceSets

        {

            get { return this.resourceSets.Values; }

        } 

     

        public IEnumerable<ServiceOperation> ServiceOperations

        {

            //这里我们暂不实现服务操作

            get { yield break; }

        } 

     

        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 IEnumerable<ResourceType> Types

        {

            get { return this.resourceTypes.Values; }

        }

    }

    下面是我们之前提到的GetMetadataProvider方法的实现例子:

    public override IDataServiceMetadataProvider GetMetadataProvider(Type dataSourceType)

    {

        DSPMetadataProvider metadata = new DSPMetadataProvider();

        var productType = new ResourceType(

            typeof(Product), // CLR type backing this Resource

            ResourceTypeKind.EntityType, // Entity, ComplexType etc

            null, // BaseType

            "Namespace", // Namespace

            "Product", // Name

            false // Abstract?

        );

        var prodKey = new ResourceProperty(

           "ProdKey",

           ResourcePropertyKind.Key |

           ResourcePropertyKind.Primitive,

           ResourceType.GetPrimitiveResourceType(typeof(int))

        );

        var prodName = new ResourceProperty(

           "Name",

           ResourcePropertyKind.Primitive,

           ResourceType.GetPrimitiveResourceType(typeof(string))

        );

        var prodPrice = new ResourceProperty(

           "Price",

           ResourcePropertyKind.Primitive,

           ResourceType.GetPrimitiveResourceType(typeof(Decimal))

        );

        productType.AddProperty(prodKey);

        productType.AddProperty(prodName);

        productType.AddProperty(prodPrice);

     

        metadata.AddResourceType(productType);

        metadata.AddResourceSet(

           new ResourceSet("Products", productType)

        );

        return metadata;

    }

    注意:我们在添加ResourceTypeResourceSet时,已经自动调用了冻结(SetReadOnly)方法了。

    当然,如果你想支持继承、关系和服务操作,那么做起来就比较复杂,但是你通过LINQ to Objects做就比较容易了。

    性能考虑

    你可能已经注意到,在这个接口中包含两组方法和属性,一组是返回枚举器(Enumeration),一组是使用Try方法尝试获取信息,这种设计主要是基于性能的考虑。

    大多数的服务请求都会调用TryResolveXXX这样的方法,这样你就可以延迟创建这些元数据信息。

    有些方法需要谨慎调用,例如:当调用get$metadata或者根文档时。(译者注:啥意思,没理解)。

    现在我们已经将自定义的DSP实现挂接到DataService上,并且已经实现了IDataServiceMetadataProvider接口,下一步,我们将实现IDataServiceQueryProvider接口来实现只读的数据查询。

  • 相关阅读:
    阿里云ECS服务器安装docker
    ActiveMq-常见的问题
    ActiveMq-基础知识
    java语言概述
    【日语词汇类编】自然与人:气候与环境
    【日语词汇类编】传媒与娱乐:大众传播媒介
    机器学习数学基础-线性代数
    掌握机器学习数学基础之概率统计
    机器学习理论篇:机器学习的数学基础
    计算机基础知识
  • 原文地址:https://www.cnblogs.com/tansm/p/DSP4.html
Copyright © 2011-2022 走看看