最近對WCFRIA+MVVM+Prism有了初步的認識,能夠簡單的實現一些數據庫的交互。這節主要講的是Silverlight通過domainservice和ado.net實體數據模型與數據庫的交互。本文的重點是與數據庫的交互,包括簡單的CURD,以下是實現的一些主要過程:
1.在Sql數據庫中新建userinfo表,包括的字段為id,name,age.數據庫創建的存儲過程為:

1 USE [Test] 2 GO 3 4 /****** Object: Table [dbo].[userinfo] Script Date: 04/24/2014 15:31:56 ******/ 5 SET ANSI_NULLS ON 6 GO 7 8 SET QUOTED_IDENTIFIER ON 9 GO 10 11 SET ANSI_PADDING ON 12 GO 13 14 CREATE TABLE [dbo].[userinfo]( 15 [ID] [varchar](50) NOT NULL, 16 [name] [varchar](50) NULL, 17 [age] [varchar](50) NULL, 18 CONSTRAINT [PK_userinfo] PRIMARY KEY CLUSTERED 19 ( 20 [ID] ASC 21 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 22 ) ON [PRIMARY] 23 24 GO 25 26 SET ANSI_PADDING OFF 27 GO
2.新建silverlight了應用程序,並啟用WcfRIA。為了簡便,我沒有單獨興建其他的項目。在web項目中添加model和servers兩個文件夾,model文件下主要存放ado實體數據模型,services文件夾存放domainservice。在silverlight客戶端需要新建ViewModel和Views文件夾,解決方案的截圖如下。
這里需要注意的是,ado.net實體數據模型需要進行一定設置才能引用。一般需要刪除兩個后綴為.tt的文件,並在添加模型時需要啟用編輯。
3.在silverlight服務端添加表userinfo的實體數據模型,並建立基於此模型的domainservice類。

1 [EnableClientAccess()] 2 public class DomainService1 : LinqToEntitiesDomainService<TestEntities> 3 { 4 5 // TODO: 6 // 考慮約束查詢方法的結果。如果需要其他輸入, 7 //可向此方法添加參數或創建具有不同名稱的其他查詢方法。 8 // 為支持分頁,需要向“userinfo”查詢添加順序。 9 public IQueryable<userinfo> GetUserinfo() 10 { 11 return this.ObjectContext.userinfo; 12 } 13 14 public void InsertUserinfo(userinfo userinfo) 15 { 16 if ((userinfo.EntityState != EntityState.Detached)) 17 { 18 this.ObjectContext.ObjectStateManager.ChangeObjectState(userinfo, EntityState.Added); 19 } 20 else 21 { 22 this.ObjectContext.userinfo.AddObject(userinfo); 23 } 24 } 25 public IQueryable<userinfo> GetUserByName(string name) 26 { 27 return this.ObjectContext.userinfo.Where(r=>r.name.StartsWith(name)); 28 } 29 public void UpdateUserinfo(userinfo currentuserinfo) 30 { 31 this.ObjectContext.userinfo.AttachAsModified(currentuserinfo, this.ChangeSet.GetOriginal(currentuserinfo)); 32 } 33 34 public void DeleteUserinfo(userinfo userinfo) 35 { 36 if ((userinfo.EntityState != EntityState.Detached)) 37 { 38 this.ObjectContext.ObjectStateManager.ChangeObjectState(userinfo, EntityState.Deleted); 39 } 40 else 41 { 42 this.ObjectContext.userinfo.Attach(userinfo); 43 this.ObjectContext.userinfo.DeleteObject(userinfo); 44 } 45 } 46 }
4.在服務端View文件夾中建立userview silverlight的用戶控件。前台代碼為:

1 <UserControl 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:vm="clr-namespace:SilverlightFamework.ViewModel" 7 xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 8 xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" 9 xmlns:Models="clr-namespace:SilverlightFamework.Web.Models" 10 xmlns:Services="clr-namespace:SilverlightFamework.Web.Services" 11 x:Class="SilverlightFamework.Views.UserView" 12 mc:Ignorable="d" 13 d:DesignHeight="300" d:DesignWidth="400"> 14 <UserControl.DataContext> 15 <vm:UserViewModel/> 16 </UserControl.DataContext> 17 <Grid x:Name="LayoutRoot" Background="White"> 18 <Grid.RowDefinitions> 19 <RowDefinition Height="50"/> 20 <RowDefinition Height="50"></RowDefinition> 21 <RowDefinition/> 22 </Grid.RowDefinitions> 23 <Button Content="添加" Width="50" Height="25" Command="{Binding OnInsert,Mode=OneWay}" CommandParameter="{Binding Text,ElementName=textbox1}" Margin="222,0,128,5"/> 24 <TextBox Text="{Binding UserInfo.age,Mode=TwoWay}" Width="100" HorizontalAlignment="Left" x:Name="txtage" Height="30"></TextBox> 25 <TextBox Text="{Binding UserInfo.name,Mode=TwoWay}" Width="100" Margin="105,0,195,0" x:Name="txtname" Height="30"></TextBox> 26 <sdk:DataGrid HorizontalAlignment="Left" Grid.Row="2" VerticalAlignment="Top" 27 AutoGenerateColumns="True" 28 ItemsSource="{Binding UserList,Mode=TwoWay}" SelectedItem="{Binding SelectItem,Mode=TwoWay}" /> 29 30 <TextBox Grid.Row="1" Width="50" Height="30" HorizontalAlignment="Left" x:Name="txtSearch"></TextBox> 31 <Button Grid.Row="1" Width="50" Height="30" Content="查詢" Command="{Binding OnSearch,Mode=OneWay}" 32 CommandParameter="{Binding Text,ElementName=txtSearch}" 33 Margin="60,0,0,0" HorizontalAlignment="Left"></Button> 34 <Button Width="50" Height="30" Grid.Row="1" Margin="120,0,0,0" HorizontalAlignment="Left" 35 Content="刪除" Command="{Binding OnDelete,Mode=OneWay}"></Button> 36 <Button Grid.Row="1" Width="50" Height="30" Margin="200,0,0,0" HorizontalAlignment="Left" Content="更新"></Button> 37 38 </Grid> 39 </UserControl>
這里需要講的重點是,Silverlight的MVVM設計模式將前台代碼和后台的完全分離。前台的ui只負責控件的展示,一切的事件和數據源都在前台通過綁定來實現,不需要到后台賦值。控件的綁定有三種類型:OneTime,OneWay,TwoWay.OneTime顧名思義就是一次性的綁定,對控件只能影響一次。OneWay,不是一次綁定的意思哈,它指的是單向的綁定,控件值的改變不會影響數據源。比如DataGird中,我們改動了某一個數據,但是他的數據源並沒有變化,當我們再次加載的時候,它還是顯示原來的數據。TwoWay是指雙向綁定,控件的值發生改變,數據源也會隨之發生變化。
Button控件可以綁定Command命令,不需要實現click事件,同時可以通過CommandParameter傳遞參數,也就是當這個命令發生的時候傳遞的參數。本文傳遞了需要查詢的字段值,為控件綁定的CommandParameter="{Binding Text,ElementName=txtSearch}",也就是將txtSeatrch的值傳遞過去。
5.ViewModel是系統的核心部分,它連接着View以及Services,也就是連接着數據層和表現層。在ViewModel中,可以進行一些與數據庫有關的操作和其他的相關操作。在ViewModel中新建UserViewModel類,代碼如下:
這里,我們新建了4個命令,分別對應着數據庫的曾刪改查,DelegateCommand並不是自己封裝的類,它引用自prism。構造函數里面的初始化很重要,因為View中的DataContext的內容直接來自於構造函數。有時候,我們會發現已經為某個屬性賦值了,但是在前台並沒有綁定上,問題就是出在這里。這里建議需要綁定的屬性最好都能在構造函數中初始化。初始化之后,我們就可以在其他地方賦值,前台的綁定就能夠實現。下面具體說說數據的加載、增加、刪除、更新。
(1)數據的加載
silvertlight中Datagrid綁定的實體或者集合。我們通過domainservice提供的load方法能夠獲得數據表中的實體的集合。

1 private IEnumerable<userinfo> userList; 2 public IEnumerable<userinfo> UserList 3 { 4 get 5 { 6 return userList; 7 } 8 set 9 { 10 if (value != userList) 11 { 12 userList = value; 13 if (PropertyChanged != null) 14 { 15 PropertyChanged(this, new PropertyChangedEventArgs("UserList")); 16 } 17 } 18 } 19 }
(2)數據的查詢
數據的查詢和數據的加載有相似之處,查詢是有條件的加載。這里只實現單一條件的查詢,當然可以擴展成為多條件的查詢。需要對域服務類進行一定的擴展。

1 public IEnumerable<userinfo> GetQueryList() 2 { 3 //domainservice = new DomainService1(); 4 if (!string.IsNullOrEmpty(searchText)) 5 { 6 7 LoadOperation<userinfo> loaduer = domainservice.Load(domainservice.GetUserByNameQuery(searchText)); 8 return loaduer.Entities; 9 } 10 else 11 { 12 LoadOperation<userinfo> loaduers = domainservice.Load(domainservice.GetUserinfoQuery()); 13 return loaduers.Entities; 14 } 15 } 16 public ICommand OnSearch { get; set; } 17 public void SearchData(object obj) 18 { 19 searchText = obj.ToString(); 20 UserList= GetQueryList(); 21 22 }
值得注意的是,這里我們通過命令來調用查詢方法。
(3)數據的插入
數據的插入也是通過command來實現的。

1 } 2 public ICommand OnInsert { get; set; } 3 public void AddData(object obj) 4 { 5 userinfo user= new userinfo(); 6 user.ID = Guid.NewGuid().ToString(); 7 user.name = userInfo.name; 8 user.age = userInfo.age; 9 //domainservice = new DomainService1(); 10 domainservice.userinfos.Add(user); 11 domainservice.SubmitChanges(SubmitOperation => { RefreshData(); },null); 12 }
這里在插入的時候,必須新建新的userinfo,因為我們的主鍵是自動生成的,而主鍵又是只讀的。若我們仍使用在構造函數中實例化的userinfo對象,則會跑出異常。一個新的對象可以解決這樣的問題。在插入成功后,通過lamda表達式來為屬性重新賦值,使我們添加的數據能夠及時的顯示。
(4)數據的更新
數據的更新比較簡單,它的要求是綁定的方式必須是twowa。通過domainservice.SubmitChanges()就能實現。

1 public ICommand OnUpdate { get; set; } 2 public void UpDateData(object obj) 3 { 4 userinfo user = this.SelectItem as userinfo; 5 domainservice.SubmitChanges(SubmitOperation => { RefreshData(); },null); 6 }
(5)數據的刪除
數據的刪除方法比較多,可以通過主鍵進行數據的刪除,也可以通過實現進行數據的刪除,本文是通過后者實現的。這里需要為DataGrid綁定selectitem,來獲取我們選擇行的值,在后台轉換為userinfo類型。通過domainservice.userinfos.Remove(userinfo)來實現刪除。

1 public ICommand OnDelete { get; set; } 2 public void DeleteData(object obj) 3 { 4 userinfo userinfos = this.SelectItem as userinfo; 5 domainservice.userinfos.Remove(userinfos); 6 domainservice.SubmitChanges(SubmitOperation => { RefreshData(); },null); 7 }
在進行調試的時候,發現數據的增刪改查並沒有及時的UI中顯示,后來通過重新加載的方法得意實現,不知道還有沒有更好的方法。
mvvm+prim 還有很多值得學習的地方,平時自己研究的並不是太深入。大家通過項目繼續學習。