WPF +MVVM(Caliburn.Micro)項目框架


       最近做了一個軟件,這個軟件不是網站,但是與HTML,AJAX等技術密切相關,也不是只有單純的數據庫增刪改查,還涉及到線程協調,比較復雜的文本處理……

       這樣的軟件,用OA,ERP的框架顯然是不合適的,因為這種軟件用不上權限管理,工作流這些技術。但是軟件又要操作數據庫。

       介於這些的特殊性,想來想去,還是自己搭建一個輕量級的軟件框架是比較好的。

一:C/S與B/S的選擇

1,我做的是一個購物網站的刷單軟件,有如下幾個方面的原因,我選擇了C/S程序

a,刷單軟件需要長時間的運行,不定時,不間斷的去訪問購物網站。

b,有時候有多個線程的需要。

c,程序運行時,需要操作本地文件的權限,要把關鍵頁面截圖(如添加收藏,貨比三家)保存到本地,然后上傳到服務器。

d,C#的Httpresponse,HttpRequest對象運行在不同的電腦上,有不同的外網IP,如果做成網站,Httpresponse,HttpRequest永遠只是在服務器上運行,外網IP只有一個。

2,在桌面運用程序方面,我感覺用WPF要比用WinForm好。

a,WPF的UI做出的產品肯定要比WinForm專業,這一點是誰都不能否認的。

b,WPF技術運用了XAML語法,這個比WinForm好用。

c,WPF可以用到MVVM模式,這點WinForm是永遠都做不到的,並且MVVM有比較成熟的產品(MVVMLight, Caliburn.Micro,Prism)等產品

在這兒選用的是Caliburn.Micro框架,優點在於有命名的自動匹配,發布/訂閱的消息模式,IOC的解耦,我舉兩個例子說明一下吧:

例一,IOC創建對象

我的項目 Trade.WPFClient  要用到  Trade.WPFBusinessPart 的頁面,正常情況下 Trade.WPFClient  要添加項目 Trade.WPFBusinessPart的引用。

但是用Caliburn.Micro是不用添加引用的。

只添要在引導程序中AppBootstrapper.cs中的配置加添加如下代碼:

protected override void Configure()
{
    foreach (var file in System.IO.Directory.GetFiles(System.IO.Directory.GetCurrentDirectory(), "Trade.*.dll"))
    {
        AssemblySource.Instance.Add(Assembly.LoadFile(file));
    }

    var catalog =
        new AggregateCatalog(
            AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());

    this.container = new CompositionContainer(catalog);

    var batch = new CompositionBatch();

    batch.AddExportedValue<IWindowManager>(new WindowManager());
    batch.AddExportedValue<IEventAggregator>(new EventAggregator());
    batch.AddExportedValue(this.container);
    batch.AddExportedValue(catalog);

    this.container.Compose(batch);
}

在使用的時候,用IOC的方式創建對象:

UserCenterViewModel userCenter = (UserCenterViewModel)IoC.Get<IUserCenter>();
bool? userCenterVal = IoC.Get<IWindowManager>().ShowDialog(userCenter);

根本就不用引用一個類庫,然后new一個對象。做到了高內聚,低耦合的原則。

 

例二:發布/訂閱模式編

先要定義一個接口,我的項目是以 Trade.BaseInterface  這個類庫作為我的接口庫類。

在發布頁面:

 方法一是同步UI發布事件,

 方法二是新開一個Task來發布一個事件。也就是異步發布事件。

然后在另外一個頁面接收這個發布的事件:

這樣就實現了兩個類庫之間互相不添加引用的情況下,把消息通知給另外一個類庫。

二:數據庫的持久化操作

數據庫持久化操作經歷了很多階段,

1,SqlHelper階段

2,enterprise library階段

3,Ibatis,Dapper階段

4,Linq, Nhibernate, EntityFramework階段。

這幾種操作,我在實戰項目中都使用過,猶其是EntityFramework,不管是Code Frist 模式,Mode First模式,還是Data First模式,好幾年前就用在項目中做過比較了。

但是我最認可的還是Dapper。

來一段碼:

/// <summary>
/// 根據用戶ID獲取用戶的相關信息
/// </summary>
/// <param name="dContext"></param>
/// <param name="userID"></param>
/// <returns></returns>
public TradeUserModel GetTradeUserByUserID(DataContextBase dContext, String userID)
{
    TradeUserModel result = default(TradeUserModel);
    String querySql = @"select * from TradeUser where UserID = @UserID;
                        select ROW_NUMBER() over (order by DoBindingDTime desc) as RowNo, * from Member where IsDelete = '0' and UserID = @UserID;
                        select * from Member where IsDelete = '0' and IsCurrent = '1' and UserID = @UserID ";
    var multi = dContext.Connection.QueryMultiple(querySql, new { UserID = userID }, dContext.Transaction);
    result = multi.Read<TradeUserModel>().FirstOrDefault();
    List<MemberModel> memberList = multi.Read<MemberModel>().ToList();
    MemberModel currentMember = multi.Read<MemberModel>().FirstOrDefault();
    result.MemberList = new ObservableCollection<MemberModel>(memberList);
    MoneyRepository moneyRepository = new MoneyRepository();
    decimal  totalMoneyIn = moneyRepository.GetBusinessMoneyByUserID(dContext, userID, "In", new string[] { "RechargeMoney", "" });
    decimal totalMoneyOut = moneyRepository.GetBusinessMoneyByUserID(dContext, userID, "Out", new string[] { "TakeMoney", "TaskOut", });
    decimal totalTransferMoneyIn = moneyRepository.GetBusinessMoneyByUserID(dContext, userID, "In", new string[] { "TransferMoneyIn", "" });
    decimal totalTransferMoneyOut = moneyRepository.GetBusinessMoneyByUserID(dContext, userID, "Out", new string[] { "TransferMoneyOut", "" });
    result.TotalEnableMoney = totalMoneyIn + totalMoneyOut;
    result.TotalNotEnableMoney = 0;
    result.TotalTransferMoney = totalTransferMoneyIn + totalTransferMoneyOut;
    result.CurrentMember = currentMember; // currentMember ?? new MemberModel();
    return result;
}

我一段Sql執行了三個查詢,用QueryMultiple取得不同的查詢結果,最神奇的地方,是查詢出來的字段,不用手動賦值,Dapper可以自動實現。比如

public List<InvitationModel> GetInvitationCollectionByOwner(DataContextBase dContext, InvitationModel invitationData)
{
    List<InvitationModel> result = default(List<InvitationModel>);
    String querySql = @"select ROW_NUMBER() over (order by CreateDTime desc) as RowNo
                        , tu1.UserName as OwnerInfoName, tu2.UserName as ComputerUniqueName, i.* from Invitation i
                        left join TradeUser tu1 on i.OwnerInfo = tu1.UserID
                        left join TradeUser tu2 on i.ComputerUnique = tu2.UserID
                        where OwnerInfo = @OwnerInfo;";
    result = dContext.Connection.Query<InvitationModel>(querySql, invitationData, dContext.Transaction).ToList();
    return result;
}

這樣的我查詢結果就直接 賦值給 InvitationModel 類了。

三:項目中實體對象的共用

理論上,應該要申明一個類與表的字段對應

如果是用WCF作為通信傳輸,還得聲明一個DataContract 數據契約類

如果是用的MVVM框,還得要有一個ViewModel類

三個類這間,要轉換賦值,有類似 automapper 這樣的工具,但是我還是不建議轉換去,轉換來的。

我直接把這個三個不同地方的類給共用起來。

比如我的實體類如下:

[DataContract]
public class LogModel : NotifyPropertyChanged
{
    private Guid _logID;
    [DataMember]
    public Guid LogID
    {
        get
        {
            return _logID;
        }
        set
        {
            _logID = value;
            OnPropertyChanged("LogID");
        }
    }

……

1,可以運作WCF的數據契約傳輸,

2,還可以作為MVVM的ViewModel

3,還可以與數據庫字段在ORM時相互映射


一箭三雕啊

結語:

這個項目還運用到了很多技術,如如線程的管理,解析網頁(網頁異步加載情下)是怎么處理的?

等下一篇博客再說。

如果對這個軟件感覺興趣的,可看上一篇博客。謝謝!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM