上一集说项目分层时提到了XAP包的动态下载。本篇文章主要讲述这个主题。
1.打包XAP
上集说到除了Framework层剩下的业务模块层的Client和Public.SL项目都要在编译时打包成单独的XAP放在ClientBin下以备下载。这个操作通过脚本来完成。右键点击Web项目(XCenter.Web)属性。在生成事件页签我们加上以下脚本:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
if not exist $(ProjectDir)ClientBin\temp md $(ProjectDir)ClientBin\temp
copy $(SolutionDir)XCenter.Framework.Public.SL\Bin\Debug\XCenter.Framework.Public.dll $(ProjectDir)ClientBin
copy $(SolutionDir)XCenter.QueryEngine.Public.SL\Bin\Debug\XCenter.QueryEngine.Public.dll $(ProjectDir)ClientBin\temp
copy $(SolutionDir)XCenter.QueryEngine.Client\Bin\Debug\XCenter.QueryEngine.Client.dll $(ProjectDir)ClientBin\temp
copy $(SolutionDir)XCenter.QueryEngine.Client\Lib\Liquid.TreeView.dll $(ProjectDir)ClientBin\temp
copy $(SolutionDir)XCenter.QueryEngine.Client\Lib\Liquid.Menu.dll $(ProjectDir)ClientBin\temp
copy $(SolutionDir)XCenter.QueryEngine.Client\Lib\Liquid.dll $(ProjectDir)ClientBin\temp
copy $(SolutionDir)XCenter.UI\Public\AppManifest.xaml $(ProjectDir)ClientBin\temp
$(ProjectDir)Chiron.exe /d:$(ProjectDir)ClientBin\temp /x:$(ProjectDir)ClientBin\XCenterBusinessAttach.xap
del $(ProjectDir)ClientBin\temp\XCenter.QueryEngine.Public.dll
del $(ProjectDir)ClientBin\temp\XCenter.QueryEngine.Client.dll
del $(ProjectDir)ClientBin\temp\Liquid.Menu.dll
del $(ProjectDir)ClientBin\temp\Liquid.dll
del $(ProjectDir)ClientBin\temp\Liquid.TreeView.dll
del $(ProjectDir)ClientBin\temp\AppManifest.xaml
rd $(ProjectDir)ClientBin\temp
上面的脚本都很简单,主要目的就是把要进行打包的DLL和XML拷贝到ClientBin目录并用Chiron.exe这个文件进行打包后删除相应的拷贝。这里要说明的是Chiron.exe是DLR中的一个XAP打包程序,需要的朋友可以去CodePlex上下载DLR。AppManifest.xml是要打包的XAP的描述文件,拆开每个XAP包都会有这么一个清单文件。我们也会依据这个文件进行XAP包的装载。
AppManifest.xml文件内容如下:
清单文件Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Deployment.Parts>
<AssemblyPart x:Name="Liquid.TreeView" Source="Liquid.TreeView.dll" />
<AssemblyPart x:Name="Liquid.Menu" Source="Liquid.Menu.dll" />
<AssemblyPart x:Name="Liquid" Source="Liquid.dll" />
<AssemblyPart x:Name="XCenter.QueryEngine.Public" Source="XCenter.QueryEngine.Public.dll" />
<AssemblyPart x:Name="XCenter.QueryEngine.Client" Source="XCenter.QueryEngine.Client.dll" />
</Deployment.Parts>
</Deployment>
这里要说明一下清单中的文件XCenter.QueryEngine.Public就是项目中的XCenter.QueryEngine.Public.SL,命名空间和程序集名称相同而已。
$(ProjectDir)Chiron.exe /d:$(ProjectDir)ClientBin"temp /x:$(ProjectDir)ClientBin"XCenterBusinessAttach.xap就是打包XAP的命令。
Chiron.exe 源目录 目标文件.xap ,打包后的效果见下图
2.动态下载XAP包
动态下载XAP包的操作在应用启动时用WebClient进行。
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
private void Application_Startup(object sender, StartupEventArgs e)
{
Grid root = new Grid();
Login loginPage = new Login();
root.Children.Add(loginPage);
this.RootVisual = root;
//开发调试阶段不留缓存
if (ClientEnvironment.IsDebug)
StorageService.ClearClientCache();
if (StorageService.NeedDownload())
{
ForceConfirm confirm = new ForceConfirm("点击确认下载缓存.", new OnForceConfirm(Confirm_OnConfirm));//TODO:多语处理
confirm.Title = "系统提示"; //TODO:多语处理
confirm.Visibility = Visibility.Visible;
WindowHelper.CurrentPage.DialogPanel.Children.Add(confirm);
}
else
{
//直接从本地缓存中加载必须的程序集
WindowHelper.ShowLoading("正在装载缓存
");
StorageService.LoadAssemblies();
WindowHelper.HideLoading();
}
}
其中StorageService先检查是否需要下载缓存,也就是本地是否已经存在了要下载的XAP包
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
/// <summary>
/// 判断是否需要下载客户端缓存,
/// 如果缓存目录不存在或是要下载的DLL不全都返回true
/// </summary>
/// <returns></returns>
public static bool NeedDownload()
{
try
{
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
string path = System.IO.Path.Combine(ClientConst.Cache_Directory,ClientConst.Cache_Xap);
if (!store.DirectoryExists(ClientConst.Cache_Directory) || !store.FileExists(path))
{
//缓存目录不存在,不用说了需要下载
return true;
}
return false;
}
}
catch(Exception ex)
{
throw new IsolatedStorageException("Check isolated storage exception.",ex);
}
}
下载的主要逻辑我封装在了Downloader类中
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
/// <summary>
/// 客户端缓存下载类
/// </summary>
public class Downloader
{
#region prop
private string xapUri;
private bool isRelative;
private WebClient wc;
public event EventHandler<XapEventArgs> XapDownloaded;
/// <summary>
/// XAP包路径
/// </summary>
public string XapUri
{
get { return xapUri; }
}
/// <summary>
/// 是否为相对路径
/// </summary>
public bool IsRelative
{
get { return isRelative; }
}
#endregion
#region constr.
public Downloader(string xapUri, bool isRelative)
{
this.xapUri = xapUri;
this.isRelative = isRelative;
wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(WebClient_OpenReadCompleted);
}
public Downloader(string xapUri):this(xapUri,true)
{
}
#endregion
/// <summary>
/// 下载完成执行方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void WebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Result == null)
throw new IsolatedStorageException("Download cache exception");
//写入客户端缓存
StorageService.CreateCache(e.Result, ClientConst.Cache_Directory, ClientConst.Cache_Xap);
LoadAssembly(e.Result);
//引发下载完成事件
if (XapDownloaded != null)
{
XapEventArgs args = new XapEventArgs();
XapDownloaded(sender,args);
}
}
/// <summary>
/// 开始下载
/// </summary>
public void StartDownload()
{
UriKind kind = isRelative ? UriKind.Relative : UriKind.Absolute;
wc.OpenReadAsync(new Uri(xapUri,kind));
}
/// <summary>
/// 分析流中的XAP包,装载到AppDomain
/// </summary>
private void LoadAssembly(System.IO.Stream xapStream)
{
//直接从下载流中读AppManifest.xml读不到,不知道为什么
//可能是Silverlight2.0的一个Bug
//StorageService.LoadAssemblies(xapStream);
StorageService.LoadAssemblies();
}
}
代码都很简单,不用多说。主要实现的功能就是判断如果客户端的独立存储中如果没有指定的XAP包,那么就用WebClient进行下载。下载完毕分析XAP包中的AppManifest.xml文件,找出其中的DLL文件后用AssemblyPart装载进AppDomain。
相关文件在这里 源代码