[SL] Folder项目_总结回顾

I:项目描述:利用Silverlight+WCF技术,模拟资源管理器(如图1)功能,通过地址栏输入本地文件夹路径,然后将解析出来的该目录下所有文件(夹)存储到数据库中,然后再加载到界面上显示出来;
[SL] Folder项目_总结回顾_第1张图片

(图1)

II:涉及技术:Silverlight;WCF;Component One控件(TreeView和FlexGrid)
III:开发环境:Visual Studio2012;SQL Server 2008 R2;Silverlight 5;Component One C1Silverlight_2014v1;

IV:项目时间:2014/08/21-2014/09/04

 

一  项目架构(如图2)

(图2)

FolderDB:DAL(数据访问层),包括文件属性类Folders,文件夹树模型类FolderModel,数据库操作类DBHelper;

FolderUI:Silverlight客户端,基本界面显示,包括FlexGrid标题数据源类ColumnHeaders,文件大小值转换类FileSizeConverter;

FolderWCF:WCF服务层,包括FolderService服务类;

FolderWeb:Web层,该项目中涉及不多;

二  源码部分解析

  1 /// <summary>
  2 /// 执行多条SQL语句,实现数据库事务
  3 /// </summary>
  4 /// 多条SQL语句
  5 public int ExecuteSqlTran(List sqlStrList)
  6 {
  7     SqlTransaction sqlTran = null;
  8     try
  9     {
 10         if (ConnectionState.Open != m_SqlConn.State)
 11         {
 12             m_SqlConn.Open();
 13         }
 14         int count = 0;// 影响的记录数
 15         SqlCommand cmd = new SqlCommand();
 16         sqlTran = m_SqlConn.BeginTransaction();
 17         cmd.Connection = m_SqlConn;
 18         cmd.Transaction = sqlTran;
 19         foreach (string sqlStr in sqlStrList)
 20         {
 21             if (sqlStr.Trim().Length > 1)
 22             {
 23                 cmd.CommandText = sqlStr;
 24                 count += cmd.ExecuteNonQuery();
 25             }
 26         }
 27         sqlTran.Commit();// 提交数据库事务
 28         return count;
 29     }
 30     catch (SqlException sqlEx)
 31     {
 32         if (null != sqlTran)
 33         {
 34             sqlTran.Rollback();// 失败后回滚
 35         }
 36 
 37         throw new Exception(sqlEx.Message);
 38     }
 39 }

注意执行完ExecuteNonQuery()后,并不算完,事务在最后还需要进行提交数据库事务才行,sqlTran.Commit(),否则操作一直都不会执行,但是调试过程中也不会出现异常;

  1 public class FolderModel
  2 {
  3     /// <summary>
  4     /// 当前目录(文件夹)属性
  5     /// </summary>
  6     public Folders CurrFolder { get; set; }
  7 
  8     /// <summary>
  9     /// 子文件(夹)集合
 10     /// </summary>
 11     public ObservableCollection SubFolders { get; set; }
 12 
 13     public FolderModel()
 14     {
 15         SubFolders = new ObservableCollection();
 16     }
 17 
 18     public FolderModel(Folders folders)
 19     {
 20         CurrFolder = folders;
 21         SubFolders = new ObservableCollection();
 22     }
 23 }

这是绑定C1TreeView控件的数据源模型,其中ObservableCollection是表示一个动态数据集合,在添加项、移除项或刷新整个列表时,此集合将提供通知。SubFolders则是作为子树的绑定,CurrFolder则是作为当前节点的绑定,详细见下面的XAML代码解释;

  1 /// <summary>
  2 /// Header数据源
  3 /// </summary>
  4 public class ColumnHeader : INotifyPropertyChanged
  5 {
  6     private List m_HeaderList;
  7     public event PropertyChangedEventHandler PropertyChanged;
  8     public List HeaderList
  9     {
 10         get
 11         {
 12             return m_HeaderList;
 13         }
 14         set
 15         {
 16             m_HeaderList = value;
 17             if (PropertyChanged != null)
 18             {
 19                 PropertyChanged.Invoke(this, new PropertyChangedEventArgs("HeaderList"));
 20             }
 21         }
 22     }
 23 }

该类作为C1FlexGrid列标题即Column.Header的动态绑定数据源,HeaderList中存储的是每列的列名;该数据会从WCF服务端获取,然后绑定在客户端上显示;

  1 public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
  2 {
  3     try
  4     {
  5         int byteSize = int.Parse(value.ToString());// 单位字节
  6         int nKBSize = (int)Math.Ceiling(byteSize * 1.0 / 1024);// 转换成KB
  7 
  8         return string.Format("{0} KB", nKBSize);
  9     }
 10     catch (Exception ex)
 11     {
 12         throw new Exception(ex.Message);
 13     }
 14 }

因为要将文件大小该列的值全部由字节转换成KB,则将Size那一列绑定该转换器即可;

  1 ItemsSource="{Binding SubFolders}" // 绑定SubFolders数据源,该数据源在FolderModel类中定义,作为树数据源;
  2 ItemTemplate="{StaticResource FolderTemplate}" // 指定树每个节点的模版定义;静态资源FolderTemplate则是在<UserControl.Resources>标签中定义;
  3 HierarchicalDataTemplate:// 定义TreeView节点模版
  4 ItemsSource="{Binding SubFolders}" // 同样指定该模版数据源
  5 <TextBlock Text="{Binding CurrFolder.Name}" /> // 节点所包含的内容,绑定CurrFolder对象中Name属性值,在FolderModel类中定义;

C1FlexGrid控件:
AutoGenerateColumns="False" //绑定数据源中有些列数据并不想显示出来,只要求部分指定的列显示,则需要设置成false,阻止其自动生成列;

<c1:C1FlexGrid.Columns> // 设置每列所绑定的数据

  1 <c1:Column Binding="{Binding [2]}" HorizontalAlignment="Left" Width="200">
  2     <c1:Column.HeaderTemplate>
  3         <DataTemplate>
  4             <TextBlock Text="{Binding Path=HeaderList[2]}" />
  5         </DataTemplate>
  6     </c1:Column.HeaderTemplate>
  7 </c1:Column>

该树绑定的数据源类型为List<List<string>>,所以Binding的对象就是List<string>类型,而按以往的思路,Binding的对象是具有get,set的属性,查看了List类下有public T this[int index] { get; set; },所以可以直接通过[n]索引来绑定数据;

HeaderTemplate作为Header的模版定义,绑定的数据源类型是ColumnHeader,直接访问其属性HeaderList即可;

 

添加文件夹树节点单击事件,在FlexGrid中加载该文件夹下所有文件信息;

  1 // 文件夹目录上的选择单个文件夹事件
  2 private void FoldersTree_SelectionChanged(object sender, SelectionChangedEventArgs e)
  3 {
  4     C1TreeViewItem selectedNode = tvFoldersTree.SelectedItem;
  5     if (null != selectedNode)
  6     {
  7         // 当前选择的节点
  8         FolderModel selectedFolderModel = (FolderModel)selectedNode.Header;
  9         m_SelectedFolderName = selectedFolderModel.CurrFolder.Name;// 节点名称(文件夹名)
 10         m_WCFClient.GetFilesAsync(selectedFolderModel.CurrFolder.ID);
 11     }
 12 }
 13 
 14 void GetFilesCompleted(object sender, GetFilesCompletedEventArgs e)
 15 {
 16     if (null == e.Error)
 17     {
 18         List<List<string>> result = e.Result;
 19         ColumnHeader columnHeader = new ColumnHeader();
 20         columnHeader.HeaderList = result[0];
 21         fgFilesView.ColumnHeaders.DataContext = columnHeader;// 绑定列标题数据源
 22 
 23         result.RemoveAt(0);// 移除第一个表示列名称的List
 24         fgFilesView.ItemsSource = new PagedCollectionView(result);// 绑定列内容数据源
 25         fgFilesView.Columns[3].ValueConverter = new FileSizeConverter();
 26         txtMsg.Text = "加载文件夹【" + m_SelectedFolderName + "】目录下的文件已完成。";
 27     }
 28     else
 29     {
 30         txtMsg.Text = "加载文件数据失败,异常信息:" + e.Error.Message;
 31     }
 32 }

    C1TreeView的节点是C1TreeViewItem类型,所以获得当前点击的节点可以用:C1TreeViewItem selectedNode = tvFoldersTree.SelectedItem;  当然也可以由sender转型得到;值得注意的是,在这里,C1TreeViewItem的Header属性是C1TreeView所绑定数据源的类型,即FolderModel,而在xaml文件中,对Column标签的header属性进行值绑定时,其header值为string类型!!故在xaml中对header的绑定是通过HeaderTemplate模版定义来完成的;

    服务器端返回的e.Result是一个List<List<string>>类型,其中第一个List<string>指的是列名;列名的绑定方式:fgFilesView.ColumnHeaders.DataContext = columnHeader;  C1FlexGrid的绑定方式:fgFilesView.ItemsSource = new PagedCollectionView(result);  其中DataContext和ItemsSource两种是有区别的,本人还没有学习到,读者可以阅读他人的相关博文学习;

    对第三列的文件大小需要绑定值转换器:fgFilesView.Columns[3].ValueConverter = new FileSizeConverter(); 即可;

  1 <configuration>
  2   <system.serviceModel>
  3     <bindings>
  4       <basicHttpBinding>
  5         <binding name="BasicHttpBinding_IFolderService" maxBufferSize="2147483647"
  6           maxReceivedMessageSize="2147483647">
  7           <security mode="None" />
  8         </binding>
  9       </basicHttpBinding>
 10     </bindings>
 11     <client>
 12       <endpoint address="http://localhost:54416/FolderService.svc"
 13         binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IFolderService"
 14         contract="FolderServiceRefer.IFolderService" name="BasicHttpBinding_IFolderService" />
 15     </client>
 16   </system.serviceModel>
 17 </configuration>
ServiceReferences.ClientConfig

    该文件是由Silverlight客户端添加服务引用时自动生成的配置文件,其中<bindings>标签内容是由WCF服务器端的Web.config配置文件中对应的<bindings>标签内容截取部分而来,对于maxBufferSize和maxReceivedMessageSize两个属性最好设置值大一些,而且必须值一样不能超过2147483647;否则会报如下异常:

    1:“由于正在缓冲消息,MaxReceivedMessageSize 和 MaxBufferSize 的值必须相同”;

    2:发生了System.ServiceModel.ProtocolException

        Message=已超过传入消息(65536)的最大消息大小配额。若要增加配额,请使用相应绑定元素上的MaxReceivedMessageSize属性。

         Source=System.ServiceModel

         StackTrace:

             在System.ServiceModel.Channels.HttpInput.ThrowHttpProtocolException(String messagem, HttpStatusCode statusCode, String statusDescription)

    <endpoint>是客户端终结点设置,address属性指WCF所在服务器端的访问路径,其他属性内容也是从服务器端Web.config配置文件中自动生成过来的;

三  项目遇到的问题及参考解决方法

1:WCF的独立调试

参考解决方法:将WCF项目“设为启动项目”,并将svc文件“设为起始页”,然后按“F5”即可打开“WCF测试客户端”,如下图,然后选择要是调试的方法(如SaveFolders()),在右侧的“值”中填入所需参数(字符串里需要转义),再执行“调用”即可在“响应”中看到结果值;如果在VS中相应的方法中加入断点,则可以进行断点调试操作!

[SL] Folder项目_总结回顾_第2张图片

2:WCF测试客户端的配置文件

参考解决方法:上图中,在“配置文件”上右键选择“用SvcConfigEditor进行编辑”选项,即可对WCF测试客户端的配置文件进行修改;

尤其是出现“已超过传入消息(65536)的最大消息大小配额。”的异常信息时,需要修改其中“绑定”目录下已定义的basicHttpBinding属性配置信息,将其中的MaxBufferPoolSize和MaxBufferSize和MaxReceivedMessageSize的值均改为int的MaxValue即可;

还有,每次打开该SvcConfigEditor,程序都会自动重新加载“个人文件夹\AppData\Local\Temp\Test Client Projects\11.0\f868aff1-aa2d-4418-abaf-f4be03a71529”下的Client.dll.config配置文件,f868aff1-aa2d-4418-abaf-f4be03a71529这个根据情况会有所变动,这样之前的属性配置修改就白搭了,每次启动都要修改,这丫的肯定不干了呀,在WCF测试客户端里,在“工具”-》“选项”中,把“始终在启动服务时重新生成配置”选项去掉就欧克了!

3:没有异常,重新生成解决方案也都成功,但是启动Silverlight后界面上完全空白

参考解决方法:毫无头绪的bug呀,而且是深藏不露的!通过启动Web端的界面也无法显示Silverlight程序,而且查看了Silverlight中xaml文件下也无异常情况,此时此刻,可以肯定的是加载Silverlight过程中出错了,没有正常加载Silverlight!但是又如何知道是什么bug,什么地方出现了bug呢?

在Web项目中找到要加载Silverlight应用程序的aspx页面文件,将onSilverlightError该函数内的errMsg错误信息弹出显示即可;

这时候再根据错误提示去找度娘就简单多了,2103的错误解决如下:

由于在修改Silverlight客户端的命名空间时,导致项目内部没有彻底完全的修改完命名空间,才会导致2103错误;在Silverlight项目右键属性中,查看“启动对象”是否是修改命名空间后的选项即可;

[SL] Folder项目_总结回顾_第3张图片

 

4:在Silverlight客户端添加服务引用时,异常“找不到类型Project.Web.Service,它在ServiceHost指令中提供为Service特性值,或在配置元素...”

[SL] Folder项目_总结回顾_第4张图片

如果,我说的是如果,你已经完全确定你的WCF服务完全正确,Web.config配置里也完全正确,那这个错误只可能是因为你在WCF项目中编辑完服务之后,没有对WCF项目“重新生成”导致的!!这也提醒着大家,在项目编码过程中,每次对WCF项目进行编辑修改后,都要重新生成一下,并且在Silverlight客户端的服务上右键“更新服务引用”才行,不然你改了半天的WCF服务端方法,结果执行的结果依然让人寒心,那就真的寒心了... ...

5:项目的部署问题

VS默认是采用自带的IIS Express来部署Silverlight项目以及WCF服务,部署后在右下角会出现如下图标和内容;

[SL] Folder项目_总结回顾_第5张图片

关于IIS Express和本地的IIS的区别,是一个很有意思的话题,这里就不说了,建议去看大神博客解析,会受益匪浅的。这里简单提及一些基本的关于IIS Express知识:

配置文件:C:\Program Files\IIS Express\AppServer\applicationhost.config

<applicationPools>标签    ----    应用程序池配置

    <add name="Clr4IntegratedAppPool" managedRuntimeVersion="v4.0" managedPipelineMode="Integrated" CLRConfigFile="%IIS_BIN%\config\templates\PersonalWebServer\aspnet.config" autoStart="true" />

    相当于IIS中

    [SL] Folder项目_总结回顾_第6张图片

<sites>标签    ----    网站配置,每一个<site>子标签,代表一个网站的配置

  1 <site name="Development Web Site" id="1" serverAutoStart="true">
  2     <application path="/">
  3         <virtualDirectory path="/" physicalPath="%IIS_BIN%\AppServer\empty_wwwroot" />
  4     </application>
  5     <bindings>
  6         <binding protocol="http" bindingInformation=":8080:localhost" />
  7     </bindings>
  8 </site>

<defaultDocument>标签    ----    设置默认起始页

  1 <files>
  2     <add value="Default.htm" />
  3     <add value="Default.asp" />
  4     <add value="index.htm" />
  5     <add value="index.html" />
  6     <add value="iisstart.htm" />
  7     <add value="default.aspx" />
  8 </files>

其他的可以自行了解去了!

 

Silverlight+WCF部署在本地IIS上,需要注意事项:

1)WCF项目和SilverlightWeb项目发布到IIS网站下的相同目录下,


[SL] Folder项目_总结回顾_第7张图片

2:作为第二次发布的项目,不能选择“发布前删除所有现有文件”,否则会把前一个发布的文件清除了;


3:发布目录下的配置文件Web.config检查一下是不是WCF项目中的配置文件,如果不是,手动粘贴过来覆盖

4:修改ClientBin\*.xap\ServiceReferences.ClientConfig文件中,关于WCF服务地址的终结点配置,改为本地ip地址;

5:应用程序“DEFAULT WEB SITE”中的服务器错误;HTTP 错误 403.14-Forbidden;Web服务器被配置为不列出此目录的内容。

因为部署的项目中没有默认的启动页面,IIS设置的默认启动页面文档包括【“Default.htm”,“Default.asp”,“index.htm”,“index.html”,“iisstart.htm”,default.aspx】,而你部署的项目根目录下没有这些文档中的至少一个,而且,在IIS功能视图中的“目录浏览”设置里也禁用了,所以报此错误!解决方法自己去搞定吧!!

[SL] Folder项目_总结回顾_第8张图片

[SL] Folder项目_总结回顾_第9张图片

6:直接访问svc服务文件(http://192.168.0.64/FolderService.svc)报如下异常:

当前已禁用此服务的元数据发布。

如果之前在VS上调试一切正常,只是发布后出现这个问题,去发布目录下查看配置文件Web.config,看是否是WCF项目下的那个配置文件,不是的话就手动拷贝覆盖。

否则的话就按浏览器下面出现的异常提示解决:

[SL] Folder项目_总结回顾_第10张图片

你可能感兴趣的:([SL] Folder项目_总结回顾)