zoukankan      html  css  js  c++  java
  • SilverLight商业应用程序开发学习笔记(2)

    从服务器暴露数据

    何为WCF RIA服务

    RIA服务建立在WCF顶层,用于建立与服务器可沟通的数据驱动Silverlight应用程序.可以描述如下

    • 数据为中心的设计模式
    • 可提供高级数据管理,授权管理以及查询功能的框架
    • 通过代码生成器,通用代码可供多层调用
    • 基于WCF服务构建,可以充当中间层与显示层的通信媒介

    image

    RIA服务聚焦中间层,与EF框架协同,是SilverLight数据应用程序的最佳选择.RIA服务是一种端到端技术;因此,需要对服务器与客户端分别进行控件才能有效工作.RIA服务包括如下功能:

    • 数据源控件,可用于XAML与服务进行通信
    • 跟踪客户端对数据所做的修改,并将其更新到服务器
    • 可以在客户端使用LINQ查询,然后在服务器端执行这些查询
    • 随时可用的授权服务功能

    WCF RIA服务的使用方法

    1、链接SilverLight与Web项目:RIA服务需要项目结构遵循一定的模式。首先,服务器项目(web)和客户端项目(silverlight)必须进行连接,使RIA服务内置的代码生成器可以在客户端项目中生成代码。这就要求客户端与服务器的项目必须在同一解决方案里。

    2、创建域服务:域服务更像是一个标准的WCF服务,但遵循一些给定的模式并提供了基本的功能。一般来说,对每个数据集都应配置一个相应的域服务,比如products,customers等;

    3、在域服务中创建域操作:主要是实现CRUD功能,使用客户端与服务器端进行交互。例如,对Product域服务通常需要配置如下域操作:GetProducts,InsertProduct,UpdateProduct和DeleteProduct;

    4、使用验证逻辑和其他特性标记修改实体:域服务所暴露的实体可以使用特性进行修饰(比如数据验证规则),也可以直接将属性设置到关联类上(称之为元数据类)。

    5、设置可供服务器与客户端共享的代码:只需要将想要共享的代码放置在以.shared.cs扩展名的文件中即可;

    6、在客户端项目调用域服务:RIA服务自动在客户端项目生成代码,这些代码可以与服务器上的域服务进行交互。RIA服务为每个域服务创建了一个域上下文,为每个域服务暴露的实体创建了相应的代理类。现在就可以使用域上下文(domain contexts)从服务器上获取数据,对数据进行处理,并将任何变更保存到服务器上。

    WCF RIA服务器如何生成代码

    EIA服务需要服务器与客户端项目必须处于同一解决方案中并彼此连接。服务器端包含暴露数据的服务,而客户端与服务器端进行通信并使用数据。

    在服务器端创建域服务以后,RIA服务就创建了代码生成任务(一般称为投影),会生成相应代码和相关代码数据对象类(一般称这为实体,在客户端任何实体都是由服务所暴露的,可以应用特性,如验证逻辑;也可以将代码进行复制并标记为共享代码,供服务器端项目与客户端项目共享使用)。

    如图所示:在SilverLight项目(客户端)有一个Generated_Code文件夹,这是在Silverlight项目编译时由RIA服务建立的。这可以命名客户端项目可以访问所有由RIA服务暴露的操作,数据,逻辑。注意生成的代码不要手工修改,重新编译后会被覆盖掉,如果想要更改,因为生成的代码是部分类,可以根据需要在其他位置编写代码。

    创建RIA服务连接

    如果使用SilverLight业务应程序项目模板,默认已经配置了RIA服务,提供了一些基本功能。

    image

    上图中项目中有一个Services文件夹与Models文件夹。Service文件夹已经包含了两个域服务(AuthenticationService和UserRegistrationService),用于对客户端的授权以及用户注册。Models文件夹包括两个数据类(User和RegistrationData),用于在服务器与客户端进行传递。在Models文件夹下还有一个Shared文件夹,有一个文件User.shared.cs,包含有可供服务器与客户端项目共享的代码。

    RIA服务的连接配置方法如下:

    image

    如果想要在现有的Silverlight项目与Web项目间使用RIA服务,可以使用这一属性进行手工连接。

    域服务

    创建RIA服务连接后,就需要暴露来自于服务器的数据与操作。达成此目的,需要使用域服务,典型的域服务包括基本的CRUD操作和任何可在客户端调用的其他定制操作。

    创建域服务最简单的方法是使用Domain Service Item模板和Add New Domain Service Class向导,

    注:在运行向导之前已经通过EF框架生成了实体类和上下文类(ObjectContext);

     image

    对这个对话框解释一下:

    “可用的DataContext/ObjectContext”下拉列表:该下拉列表用于选择EF模型;如果模型未显示,请先生成项目;如果使用POCO类,应选择<空域服务类>选项,然后手工实现相关的域操作。

    实休列表:列出了所有可供处理的实体。选定的实体在域服务中会创建操作,该操作返回相应实体的集合。如果想要修改实体,则勾选”Enable Editing“选项,然后向导会为每个实体创建insert,update和delete域操作。

    Enabling Client Access:必须进行选定,这个选项会在域服务类上装饰EnableClientAccess特性标识,用来提示RIA服务的代码生成器应该在客户端项目中生成代码。

    Exposing an OData Endpoint :这个选项可以用于SharePoint,由于对其研究不多,不再赘述;默认不需要选择此选项。

    Generating Associated Classes for Metadata :为每个选定的实体生成元数据类,可以在该类中对实体的类、属性进行特性标记修饰,而无需修改实体本身。元数据类扩展名为.metadata.cs,例如ProductService域服务会创建ProductService.metadata.cs文件,包含有所有该域服务暴露的实体的所有元数据类,

    域操作

    域操作是域服务上的方法,可以在客户端进行调用。除了常规的CRUD以外,也可以设置定制的方法。Add New Domain Service Class wizard 创建了CRUD功能的方法,理解了这些方法,就可以用来创建自已的方法了。为了使用RIA服务能够正确地在客户端项目中生成相应的代码,默认情况下需要执行一些的规则,包括方法命名和方法签名等(当然也可以定制自已的方法命名和签名规则,但需要在在方法上加注特性标记以指明是哪种操作模式)。

    下面是各类操作的默认方法:

    查询:

    命名规则/方法签名规则:查询操作无命名规则,可接受任何参数或无参数,因为可以被RIA服务隐匿识别为查询操作,但是最好返回IQueryable<T>, IEnumerable<T>,或单个实体,并对方法修饰Query标记;

    返回集合的方法:

    public IQueryable<Product> GetProducts() 
    { 
        return this.ObjectContext.Products; 
    }
    public IQueryable<Product> GetProducts(string nameFilter) 
    { 
        return this.ObjectContext.Products.Where(p =>  
                                                 p.Name.StartsWith(nameFilter)); 
    }

    返回值实现IQueryable<T>接口,可以直接应用LINQ to Entities,并且可以实现RIA服务提供的很多有用的功能。

    返回单个实体

    public Product GetProduct(int productID) 
    { 
        return ObjectContext.Products 
            .Where(p => p.ProductID == productID) 
            .FirstOrDefault(); 
    } 

    Insert/Update/Delete操作

    命名、方法签名规则:

    insert/update/delete操作必须接受一个单个实体作为参数,并且无附加参数,无返回值;并且在命名方法时应注意:

    • Insert操作方法必须开始于Insert,Create,Add。否则需要在方法上修饰Insert标记;
    • Update操作方法必须开始于Update,Change或Modify。否则需要在方法上修饰Update标记;
    • Delete操作方法必须开始于Delete或Remove。否则需要在方法上修改Delete标记;

    Insert/Update/Delete操作的域服务循环

    • authorize changeset阶段:检查每个对changeset调用的操作是否遵循安全相关的规则,这些规则在域服务的操作上定义,方法是在操作上修饰安全规则特性标识。
    • validate changeset 阶段:检查验证规则
    • execute changeset 阶段:执行
    • persist changeset 阶段:调用SubmitChanges方法,将数据持久化到数据库;

    每个步骤都由域服务自动执行;但是,也有相应的方法可以覆写从而跳过某几个步骤进行定制,这些方法包括:

    • AuthorizeChangeSet
    • ValidateChangeSet
    • ExecuteChangeSet
    • PersistChangeSet

    Insert/Update/Delete操作例子:

    插入数据:

    public void InsertProduct(Product product) 
    { 
        if (product.EntityState != EntityState.Added) 
        { 
            if (product.EntityState != EntityState.Detached) 
            { 
                this.ObjectContext.ObjectStateManager.ChangeObjectState(product,  
                                                               EntityState.Added); 
            } 
            else 
            { 
                this.ObjectContext.AddToProducts(product); 
            } 
        } 
    }

    更新数据:

    public void UpdateProduct(Product currentProduct) 
    { 
        if (currentProduct.EntityState == EntityState.Detached) 
        { 
            this.ObjectContext.Products.AttachAsModified(currentProduct,  
                                    this.ChangeSet.GetOriginal(currentProduct)); 
        } 
    } 

    删除操作

    public void DeleteProduct(Product product) 
    { 
        if (product.EntityState == EntityState.Detached) 
        { 
            this.ObjectContext.Attach(product); 
        } 
        this.ObjectContext.DeleteObject(product); 
    } 

    调用操作

    调用操作通常用于实例化服务器上的行为,比如请求服务器发送邮件,或者请求服务器返回一个简单的值(该值通常不需要对变更进行跟踪),比如汇率。与查询操作一样,异步调用操作也可以由客户端应用直接调用,这些调用是立即完成的,不像常规操作那样需要changeset提交到服务器上才进行调用。

    尽管调用操作概念上来说与普通的WCF服务操作相同,但是它们不能返回复杂的类型,除了实体对象外。这可能是使用RIA服务所要面对的一个非常大的问题。这意味着要向客户端返回复杂的对象类型是不可能的,解决这个问题的唯一方法是用回到纯WCF通信,希望这个问题在将来能得以克服。

    调用操作不需要命名或方法签名约定(惯例),实质上任何领域服务中的操作,只要不属于任何其他类型的操作分类(通过命名或方法签名)就会被RIA服务认为是一个调用操作。也可以通过为方法应用Invoke来显示将一个方法作为调用方法。调用操作的示例如下所示:

    public decimal GetExchangeRate(string fromCurrency, string toCurrency) 
    { 
        return ExchangeRates.GetRate(fromCurrency, toCurrency); 
    }

    自定义操作

    自定义操作用于在一个实体上完成一些行为的操作,比如,你可能有一个操作来停用一个产品,所有这些逻辑将在服务器端完成,但是当客户端使用调用操作时,会被立即执行。自定义操作不会立即执行,它会延迟执行到客户端向服务器端提交了一个变更集,可以将自定义操作看在是与插入/更新/删除相同的操作。

    当客户端生成一个自定义操作时,它将作为实体的一个方法被创建,然后对那个实体发生作用,同时它也被创建在实体上下文级别,以便进行调用。

    自定义方法通常是在插入或更新操作在服务器端被调用后才执行,如果一个自定义的操作在一个实体上被调用,但是随后那个实体被删除,那么自定义方法将不会被调用,而是被丢弃。

    自定义操作没有命名惯例,但是它们必须没有返回值,必须接收一个实体对象作为其第1个参数(任意个数的其它参数也是被允许的)。没有什么显然的特性来标识一个自定义操作,但是相反你应该使用Update特性,设置其UsingCustomMethod属性值为True。自定义操作示例如下所示:

    public void DiscontinueProduct(Product product) 
    { 
        // Logic to discontinue the product... 
    } 

    实体修饰

    实体修饰可以达成三个目的:控制实体在客户端的显示,应用验证规则,指定某属性的数据特性(如是否为主键,是否执行并发检查等),需要在元数据类上进行设置。

    [MetadataTypeAttribute(typeof(Product.ProductMetadata))] 
    public partial class Product 
    { 
        internal sealed class ProductMetadata  
        { 
            // Field definitions (removed for the purposes of brevity) 
        } 
    } 

    上述代码是Product实体的元数据类,特点有:是Product的部分类,ProductMetadata是Product的嵌套类(尽管这不是必须的),通过MetadatTypeAttribute特性标记标识哪个类是Product的元数据类)。

    通用特性的使用方法可参考http://msdn.microsoft.com/zh-cn/library/system.componentmodel.dataannotations.aspx以及

    http://msdn.microsoft.com/zh-cn/library/cc490428(v=vs.91).aspx,需要特别认识的新概念包括复合层次结构,RoundtripOriginal,需要另行关注与了解。

    自定义验证特性(以案例说明):

    1、在Web项目里新建一个文件夹:ValidationRules。

    2、添加一个新类到ValidationRules文件夹:ProductClassValidationAttribute.shared.cs,使用.shared意味着代码可以在服务器与客户端共享。注意该类以Attitude结尾,虽然不是必须的,但在实际使用时可以省略这个后缀;

    3、取消对System.Web名称空间的引用,因为Silverlight并不支持该名称空间,当代码被复制到SilverLight项目时(RIA代码生成器生成),会出错;

    4、添加对System。ComponentModel.DataAnnotations名称空间的引用;

    5、ProductClassValidationAttribute类应继承自ValidationAttribute类;

    6、覆写IsValid方法,虽然基类有两个IsValid方法,但是SIlverLight只支持以ValidationContext作为参数的方法;在此方法中可以编写验证逻辑代码。如果验证OK,该方法应返回ValidationResult.Success;如果验证失败,可以返回错误信息(信息内容必须作为ValidationResult类的构造器参数),代码如下:

    using System; 
    using System.ComponentModel.DataAnnotations; 
    using System.Linq; 
    namespace AdventureWorks.Web.ValidationRules 
    { 
        public class ProductClassValidationAttribute : ValidationAttribute 
        { 
            protected override ValidationResult IsValid(object value, 
                                                        ValidationContext validationContext) 
            { 
                bool isValid = true; 
     
                if (value != null) 
                { 
                    string productClass = value.ToString(); 
     
                   //验证参数值是否包含H,M,L等字母
                    string[] validClasses = new string[] { "H", "M", "L", "" }; 
                    isValid = validClasses.Contains(productClass.ToUpper()); 
                } 
     
                return isValid ? ValidationResult.Success : new ValidationResult( 
                                                    "The class is invalid"); 
            } 
        } 
    } 

    8、创建完毕后就可以在属性中使用了。在Product实体的元数据类中添加如下引用:

    using AdventureWorks.Web.ValidationRules;

    找到相应属性:

    [ProductClassValidation] 
    public string Class; 

    9、另一个例子:验证类中SellEndDate应大于SellStartDate,代码如下:

    using System; 
    using System.Linq; 
    using System.ComponentModel.DataAnnotations; 
     
    namespace AdventureWorks.Web.ValidationRules 
    { 
        public class SellDatesValidationAttribute : ValidationAttribute 
        { 
            protected override ValidationResult IsValid(object value,  
                                                ValidationContext validationContext) 
            { 
                Product product = value as Product; 
                return product.SellEndDate == null ||  
                       product.SellEndDate > product.SellStartDate ?  
                            ValidationResult.Success : new ValidationResult( 
                     "The sell end date must be greater than the sell start date"); 
            } 
        } 
    } 
  • 相关阅读:
    2019-9-2-Visual-Studio-自定义项目模板
    2018-8-10-WPF-判断调用方法堆栈
    2018-8-10-WPF-判断调用方法堆栈
    2018-8-10-VisualStudio-自定义外部命令
    2018-8-10-VisualStudio-自定义外部命令
    Java实现 LeetCode 999 车的可用捕获量(简单搜索)
    向代码致敬,寻找你的第83行(阿里巴巴的第83行代码是什么梗)
    向代码致敬,寻找你的第83行(阿里巴巴的第83行代码是什么梗)
    向代码致敬,寻找你的第83行(阿里巴巴的第83行代码是什么梗)
    Java实现 LeetCode 557 反转字符串中的单词 III(StringBuilder的翻转和分割)
  • 原文地址:https://www.cnblogs.com/qouoww/p/2487159.html
Copyright © 2011-2022 走看看