• RIA Service中对于递归实体类型处理的问题及解决方案


    故事是这样开始的:

    我们在开发一个Silverlight应用程序的时候使用到了RIA Service,我们需要通过该服务公开一个对文件夹的查询操作。

    为此,我们建立了如下的一个实体类型

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    using System.Runtime.Serialization;
    using System.ComponentModel.DataAnnotations;
    
    using System.ServiceModel.DomainServices.Server;
    
    namespace DomainServiceSample.Web
    {
        [DataContract]//必须声明类别为DataContract
        public class Folder
        {
            [DataMember]//必须声明属性为DataMember
            [Key]//一个用于DomainService的Entity必须有一个Key
            public Guid ID { get; set; }
    
            [DataMember]
            public string Name { get; set; }
    
            [DataMember]         
            public Folder[] SubFolder { get; set; }
            
        }
    }

    【注意】上面其实是有一个递归的类型,也就是Folder里面又包含Folder

    然后,我们创建了一个DomainService

    namespace DomainServiceSample.Web
    {
        using System;
        using System.Collections.Generic;
        using System.ComponentModel;
        using System.ComponentModel.DataAnnotations;
        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
        {
            [Query]
            public IQueryable<Folder> GetFolder()
            {
                var folder = new Folder() { ID = Guid.NewGuid(), Name = "Level 1 Folder" };
                var subFolders = new[]{
                    new Folder(){ID=Guid.NewGuid(),Name="Level 2 Folder"},
                    new Folder(){ID=Guid.NewGuid(),Name="Level 2 Folder 2"}
                };
                folder.SubFolder = subFolders;
                return new[] { folder }.AsQueryable();
            }
        }
    }
    
    
    

    这个代码没有什么特别的,我们计划向客户端发送的结果是一个Folder,但同时它包含了两个子Folder。

    编写上面两个类型很顺利,然后我们生成项目,因为使用了Domain Service,所以在Silverlight应用程序中会得到一个自动生成的类型

    我们打开那个文件,确实里面是有一个Folder的类型

        /// <summary>
        /// The 'Folder' entity class.
        /// </summary>
        [DataContract(Namespace="http://schemas.datacontract.org/2004/07/DomainServiceSample.Web")]
        public sealed partial class Folder : Entity
        {
            
            private Guid _id;
            
            private string _name;
            
            #region Extensibility Method Definitions
    
            /// <summary>
            /// This method is invoked from the constructor once initialization is complete and
            /// can be used for further object setup.
            /// </summary>
            partial void OnCreated();
            partial void OnIDChanging(Guid value);
            partial void OnIDChanged();
            partial void OnNameChanging(string value);
            partial void OnNameChanged();
    
            #endregion
            
            
            /// <summary>
            /// Initializes a new instance of the <see cref="Folder"/> class.
            /// </summary>
            public Folder()
            {
                this.OnCreated();
            }
            
            /// <summary>
            /// Gets or sets the 'ID' value.
            /// </summary>
            [DataMember()]
            [Editable(false, AllowInitialValue=true)]
            [Key()]
            [RoundtripOriginal()]
            public Guid ID
            {
                get
                {
                    return this._id;
                }
                set
                {
                    if ((this._id != value))
                    {
                        this.OnIDChanging(value);
                        this.ValidateProperty("ID", value);
                        this._id = value;
                        this.RaisePropertyChanged("ID");
                        this.OnIDChanged();
                    }
                }
            }
            
            /// <summary>
            /// Gets or sets the 'Name' value.
            /// </summary>
            [DataMember()]
            public string Name
            {
                get
                {
                    return this._name;
                }
                set
                {
                    if ((this._name != value))
                    {
                        this.OnNameChanging(value);
                        this.RaiseDataMemberChanging("Name");
                        this.ValidateProperty("Name", value);
                        this._name = value;
                        this.RaiseDataMemberChanged("Name");
                        this.OnNameChanged();
                    }
                }
            }
            
            /// <summary>
            /// Computes a value from the key fields that uniquely identifies this entity instance.
            /// </summary>
            /// <returns>An object instance that uniquely identifies this entity instance.</returns>
            public override object GetIdentity()
            {
                return this._id;
            }
        }

    但是,让人疑惑的是,这个类型里面并没有包含SubFolder这个属性

    这是什么情况呢?难道RIA Service不允许传递这种包含递归类型引用的实体?确实如此。

    我目前的解决方法是:

    1. 为Folder类型添加一个ParentID属性

    2. 为SubFolder设置关联,即子Folder的ParentID设置到父Folder的ID。并且定义他们的关联

    3. 使用Include属性标记SubFolder是要包含进来的

    所以,这个类型修改为下面这样

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    using System.Runtime.Serialization;
    using System.ComponentModel.DataAnnotations;
    
    using System.ServiceModel.DomainServices.Server;
    
    namespace DomainServiceSample.Web
    {
        [DataContract]//必须声明类别为DataContract
        public class Folder
        {
            [DataMember]//必须声明属性为DataMember
            [Key]//一个用于DomainService的Entity必须有一个Key
            public Guid ID { get; set; }
    
            [DataMember]
            public string Name { get; set; }
    
    
            [DataMember]
            [Association("Test","ID","ParentID")]
            [Include]
            public Folder[] SubFolder { get; set; }
    
    
            [DataMember]
            public Guid ParentID { get; set; }
            
        }
    }

    image

    然后,我们再来看在Silverlight中生成的那个类型

       /// <summary>
        /// The 'Folder' entity class.
        /// </summary>
        [DataContract(Namespace="http://schemas.datacontract.org/2004/07/DomainServiceSample.Web")]
        public sealed partial class Folder : Entity
        {
            
            private Guid _id;
            
            private string _name;
            
            private Guid _parentID;
            
            private EntityCollection<Folder> _subFolder;
            
            #region Extensibility Method Definitions
    
            /// <summary>
            /// This method is invoked from the constructor once initialization is complete and
            /// can be used for further object setup.
            /// </summary>
            partial void OnCreated();
            partial void OnIDChanging(Guid value);
            partial void OnIDChanged();
            partial void OnNameChanging(string value);
            partial void OnNameChanged();
            partial void OnParentIDChanging(Guid value);
            partial void OnParentIDChanged();
    
            #endregion
            
            
            /// <summary>
            /// Initializes a new instance of the <see cref="Folder"/> class.
            /// </summary>
            public Folder()
            {
                this.OnCreated();
            }
            
            /// <summary>
            /// Gets or sets the 'ID' value.
            /// </summary>
            [DataMember()]
            [Editable(false, AllowInitialValue=true)]
            [Key()]
            [RoundtripOriginal()]
            public Guid ID
            {
                get
                {
                    return this._id;
                }
                set
                {
                    if ((this._id != value))
                    {
                        this.OnIDChanging(value);
                        this.ValidateProperty("ID", value);
                        this._id = value;
                        this.RaisePropertyChanged("ID");
                        this.OnIDChanged();
                    }
                }
            }
            
            /// <summary>
            /// Gets or sets the 'Name' value.
            /// </summary>
            [DataMember()]
            public string Name
            {
                get
                {
                    return this._name;
                }
                set
                {
                    if ((this._name != value))
                    {
                        this.OnNameChanging(value);
                        this.RaiseDataMemberChanging("Name");
                        this.ValidateProperty("Name", value);
                        this._name = value;
                        this.RaiseDataMemberChanged("Name");
                        this.OnNameChanged();
                    }
                }
            }
            
            /// <summary>
            /// Gets or sets the 'ParentID' value.
            /// </summary>
            [DataMember()]
            public Guid ParentID
            {
                get
                {
                    return this._parentID;
                }
                set
                {
                    if ((this._parentID != value))
                    {
                        this.OnParentIDChanging(value);
                        this.RaiseDataMemberChanging("ParentID");
                        this.ValidateProperty("ParentID", value);
                        this._parentID = value;
                        this.RaiseDataMemberChanged("ParentID");
                        this.OnParentIDChanged();
                    }
                }
            }
            
            /// <summary>
            /// Gets the collection of associated <see cref="Folder"/> entity instances.
            /// </summary>
            [Association("Test", "ID", "ParentID")]
            public EntityCollection<Folder> SubFolder
            {
                get
                {
                    if ((this._subFolder == null))
                    {
                        this._subFolder = new EntityCollection<Folder>(this, "SubFolder", this.FilterSubFolder);
                    }
                    return this._subFolder;
                }
            }
            
            private bool FilterSubFolder(Folder entity)
            {
                return (entity.ParentID == this.ID);
            }
            
            /// <summary>
            /// Computes a value from the key fields that uniquely identifies this entity instance.
            /// </summary>
            /// <returns>An object instance that uniquely identifies this entity instance.</returns>
            public override object GetIdentity()
            {
                return this._id;
            }
        }

    这时就看到SubFolder了,而且还包含了很多其他的属性。

    最后,我做了一个界面来显示给大家看看效果

    MainPage.xaml的内容如下

    <UserControl x:Class="DomainServiceSample.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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
    
        <Grid x:Name="LayoutRoot" Background="White">
            <sdk:DataGrid AutoGenerateColumns="True" Margin="16,13,12,12" Name="dataGrid1" ItemsSource="{Binding}" RowDetailsVisibilityMode="Visible">
                <sdk:DataGrid.RowDetailsTemplate>
                    <DataTemplate>
                        <sdk:DataGrid AutoGenerateColumns="True" Margin="20,20,20,20" Height="300" ItemsSource="{Binding SubFolder}" />
                    </DataTemplate>
                </sdk:DataGrid.RowDetailsTemplate>
            </sdk:DataGrid>
        </Grid>
    </UserControl>
    

    MainPage.xaml.cs的内容如下

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    
    using DomainServiceSample.Web;
    
    namespace DomainServiceSample
    {
        public partial class MainPage : UserControl
        {
            public MainPage()
            {
                InitializeComponent();
    
                Loaded += new RoutedEventHandler(MainPage_Loaded);
            }
    
            void MainPage_Loaded(object sender, RoutedEventArgs e)
            {
                var ctx = new SampleDomainContext();
                var op = ctx.Load<Folder>(ctx.GetFolderQuery());
                dataGrid1.DataContext = op.Entities;
            }
        }
    }
    

    同时,服务端的代码我也稍作了修改

    namespace DomainServiceSample.Web
    {
        using System;
        using System.Collections.Generic;
        using System.ComponentModel;
        using System.ComponentModel.DataAnnotations;
        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
        {
            [Query]
            public IQueryable<Folder> GetFolder()
            {
                var folder = new Folder() { ID = Guid.NewGuid(), Name = "Level 1 Folder" };
                var subFolders = new[]{
                    new Folder(){ID=Guid.NewGuid(),Name="Level 2 Folder",ParentID=folder.ID},
                    new Folder(){ID=Guid.NewGuid(),Name="Level 2 Folder 2",ParentID=folder.ID}
                };
                folder.SubFolder = subFolders;
                return new[] { folder }.AsQueryable();
            }
        }
    }
    
    
    

    调试起来看到的效果如下

    image

    虽然解决了问题,但个人感觉Domain Service这个设计值得商榷。如果各位有更好的见解和解决方案,请不吝赐教

  • 相关阅读:
    数据结构 括号法二叉树转化为二叉链表链式存储结构
    数据结构 二叉树的非递归遍历算法再回顾
    C语言算法 设计一个算法,将数组m个元素循环右移。要求算法空间复杂度为O(1)
    JAVA 递归输出所有可能的出栈序列
    C语言数据结构 头尾指针数组的综合应用
    C语言 重写strcmp函数
    C语言数据结构 判断出栈序列合法性
    PMD执行Java代码分析的原理
    Redis缓存和MySQL数据一致性方案详解
    mybtais 源码分析
  • 原文地址:https://www.cnblogs.com/chenxizhang/p/2077459.html
Copyright © 2020-2023  润新知