• 自定义Domain Service时遇到实体不能更新的问题及其解决方案


    这是在项目中的一个小问题。我们用到了自定义的Domain Service和自定义Entity,如果仅仅是读取数据,没有任何问题。但如果需要通过双向绑定,实现更新,则可能会遇到一个错误。类似下面这样

    image

    本文将重现这个问题,并分析原因和提供解决方案。

    【备注】Domain Service看起来很不错,尤其是结合LINQ to Entity的话。但在使用自定义Domain Service时会遇到各种各样的问题,很多问题是让人莫名其妙的。我个人觉得这个设计模型还需要进一步完善的

    1. 自定义业务实体

    下面这个类型会被用到服务中返回文件类型映射的信息

    using System.ComponentModel.DataAnnotations;
    
    namespace SilverlightApplication3.Web
    {
        public class Employee
        {
            [Key]
            public int ID { get; set; }
            [Editable(true)]
            public string FirstName { get; set; }
            [Editable(true)]
            public string LastName { get; set; }
        }
    }

    2. 自定义Domain Service

    namespace SilverlightApplication3.Web
    {
        using System.Linq;
        using System.ServiceModel.DomainServices.Hosting;
        using System.ServiceModel.DomainServices.Server;
    
    
        // TODO: Create methods containing your application logic.
        [EnableClientAccess()]
        public class SampleDomainService : DomainService
        {
            public IQueryable<Employee> GetEmployees()
            {
                return new[]{
                    new Employee(){ID=1,FirstName="ares",LastName="chen"},
                    new Employee(){ID=2,FirstName="bill",LastName="gate"}}.AsQueryable();
    
            }
        }
    }
    
    
    

    3. 客户端的ViewModel

    using System.Collections.ObjectModel;
    using Microsoft.Practices.Prism.ViewModel;
    using SilverlightApplication3.Web;
    using System.ComponentModel;
    
    namespace SilverlightApplication3.ViewModels
    {
        public class MainPageViewModel:NotificationObject
        {
    
            public MainPageViewModel()
            {
    
                if(DesignerProperties.IsInDesignTool) return;
    
                var ctx = new SampleDomainContext();
                ctx.Load<Employee>(ctx.GetEmployeesQuery(), (op) => {
                    Employees = new ObservableCollection<Employee>(op.Entities);
                }, true);
            }
    
    
            private ObservableCollection<Employee> _Employees;
            public ObservableCollection<Employee> Employees
            {
                get { return _Employees; }
                set
                {
                    if(_Employees != value)
                    {
                        _Employees = value;
                        RaisePropertyChanged("Employees");
                    }
                }
            }
    
        }
    }
    

    4. 页面定义

    <UserControl
        xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
        x:Class="SilverlightApplication3.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DesignHeight="300"
        d:DesignWidth="400"
        xmlns:vm="clr-namespace:SilverlightApplication3.ViewModels">
    
        <UserControl.DataContext>
            <vm:MainPageViewModel></vm:MainPageViewModel>
        </UserControl.DataContext>
        <Grid
            x:Name="LayoutRoot"
            Background="White"
            Margin="20">
            <StackPanel>
                <TextBlock
                    Text="Employee List"
                    FontSize="20"></TextBlock>
                <sdk:DataGrid
                    Margin="10"
                    ItemsSource="{Binding Employees}"
                    AutoGenerateColumns="False">
                    <sdk:DataGrid.Columns>
                        <sdk:DataGridTextColumn
                            Binding="{Binding ID}"
                            Header="ID"></sdk:DataGridTextColumn>
                        <sdk:DataGridTextColumn
                            Binding="{Binding FirstName,Mode=TwoWay}"
                            Header="FirstName"></sdk:DataGridTextColumn>
                        <sdk:DataGridTextColumn
                            Binding="{Binding LastName,Mode=TwoWay}"
                            Header="LastName"></sdk:DataGridTextColumn>
                    </sdk:DataGrid.Columns>
                </sdk:DataGrid>
            </StackPanel>
        </Grid>
    </UserControl>
    

    5. 测试运行

    image

    读取数据是没有问题的,但是如果我们去编辑FirstName或者LastName,就会发生如下的错误

    image

    这个错误确实让人看得云里雾里的。不是吗?你绝不会想到是要像下面这样解决

    6. 添加一个Updatexxxx方法,使得Employee这个EntitySet可以被编辑

    解决方案居然是要为Employee这个类型,添加一个特殊的方法,用Update做为前缀。看下面的例子

    namespace SilverlightApplication3.Web
    {
        using System.Linq;
        using System.ServiceModel.DomainServices.Hosting;
        using System.ServiceModel.DomainServices.Server;
    
    
        // TODO: Create methods containing your application logic.
        [EnableClientAccess()]
        public class SampleDomainService : DomainService
        {
            public IQueryable<Employee> GetEmployees()
            {
                return new[]{
                    new Employee(){ID=1,FirstName="ares",LastName="chen"},
                    new Employee(){ID=2,FirstName="bill",LastName="gate"}}.AsQueryable();
    
            }
    
            /// <summary>
            /// 这个方法用来更新员工
            /// </summary>
            /// <param name="e"></param>
            public void UpdateEmployee(Employee e)
            {
            }
    
        }
    }
    
    
    

    当然,也可以不叫UpdateEmployee的名字,但参数必须必须是Employee类型

    例如下面这样

    namespace SilverlightApplication3.Web
    {
        using System.Linq;
        using System.ServiceModel.DomainServices.Hosting;
        using System.ServiceModel.DomainServices.Server;
    
    
        // TODO: Create methods containing your application logic.
        [EnableClientAccess()]
        public class SampleDomainService : DomainService
        {
            public IQueryable<Employee> GetEmployees()
            {
                return new[]{
                    new Employee(){ID=1,FirstName="ares",LastName="chen"},
                    new Employee(){ID=2,FirstName="bill",LastName="gate"}}.AsQueryable();
    
            }
    
    
            /// <summary>
            /// 这个方法用来更新员工
            /// </summary>
            /// <param name="e"></param>
            [Update]
            public void ModifyEmployee(Employee e)
            {
            }
        }
    }
    
    
    

    7.再次运行测试(客户端代码无需任何修改)

    请注意,第一行的FirstName已经被修改了(由ares改成了mike)

    image

    8. 分析问题的根源

    那么,到底发生了什么呢?为什么添加了那个方法就又可以编辑了呢

    我们可以打开客户端自动生成的那个文件来看一下,里面有一段代码很特殊

            internal sealed class SampleDomainContextEntityContainer : EntityContainer
            {
                
                public SampleDomainContextEntityContainer()
                {
                    this.CreateEntitySet<Employee>(EntitySetOperations.Edit);
                }
            }

    是在这里,在这个内部密封的类型里面,它设置了Employee这个EntitySet是可以编辑的,而其实这个EntitySetOperation是一个枚举,具有下面可用的值

    #region Assembly System.ServiceModel.DomainServices.Client.dll, v2.0.50727
    // C:\Program Files (x86)\Microsoft SDKs\RIA Services\v1.0\Libraries\Silverlight\System.ServiceModel.DomainServices.Client.dll
    #endregion
    
    using System;
    
    namespace System.ServiceModel.DomainServices.Client
    {
        // Summary:
        //     Enumeration of the types of operations permissable on an System.ServiceModel.DomainServices.Client.EntitySet.
        [Flags]
        public enum EntitySetOperations
        {
            // Summary:
            //     Only read operations are permitted, no update operations are allowed.
            None = 0,
            //
            // Summary:
            //     New entities may be added
            Add = 1,
            //
            // Summary:
            //     Entities may be updated
            Edit = 2,
            //
            // Summary:
            //     Entities may be removed
            Remove = 4,
            //
            // Summary:
            //     Entities may be added, updated and removed
            All = 7,
        }
    }
    

    9.题外话:

    我们在使用LINQ to Entity作为Domain Service的数据模型的时候,可能就意识不到这个问题,是因为我们可以在创建Domain Service的时候设置是否允许编辑

    image

    然后自动生成的Domain Service里面就自动具有了Update,Delete,Insert之类的方法,这样就允许这些业务实体支持更新了。

  • 相关阅读:
    Django笔记:上下文处理器和中间件
    Django笔记:Cookie和Session
    redhat 7.4从openssh7.6离线升级openssh8.4p1解决方法
    “应用程序无法正常启动(0xc000007)”处理办法
    "安装VMware Tools"显示灰色的解决办法
    redis 根据模板批量生成使用不同端口的配置文件并运行运行服务
    扩展 docker 管理命令
    shell getopt 讲解
    编写 Redis 测试 shell 脚本
    自定义 shell 软件安装脚本
  • 原文地址:https://www.cnblogs.com/chenxizhang/p/2110979.html
Copyright © 2020-2023  润新知