#region 同步单位
void SyncUnit() {
    ProgressContent = "正在同步单位...";
    var query = Context.GetUnitsQuery().Where(p => p.ShopUniqueId == App.CurrentShop.UniqueId);
    Context.Load(query, LoadUnitCallback, null);
}

void LoadUnitCallback(LoadOperation<RP_Unit> loadOp) {

    //服务端数据
    IEnumerable<RP_Unit> serverUnits = Context.RP_Units;
    //客户端数据
    IEnumerable<RP_Unit> clientUnits;
    //获取数据
    using (var helper = new LocalDb.UnitHelper()) {
        clientUnits = helper.GetList();
    }
    //获取交集用来同步已存在且需要更新的实体
    var intersectUnits = serverUnits.Intersect(clientUnits, new UnitEntityCompare());

    //遍历交集集合
    foreach (var item in intersectUnits) {
        // 根据交集的项目分别获取服务端及客户端需要更新的数据
        var serverUnitToUpdate = serverUnits.First(p => p.UniqueId == item.UniqueId);
        var clientUnitToUpdate = clientUnits.First(p => p.UniqueId == item.UniqueId);
        // 根据更新时间进行比较,如果相同则忽略操作
        if (clientUnitToUpdate.UpdateDate != serverUnitToUpdate.UpdateDate) {

            //如果服务端较新,则更新客户端
            if (clientUnitToUpdate.UpdateDate < serverUnitToUpdate.UpdateDate) {
                using (var helper = new LocalDb.UnitHelper()) {
                    helper.UpdateUnit(serverUnitToUpdate);
                }
            }
            //否则更新服务端
            else {
                serverUnitToUpdate.UpdateDate = clientUnitToUpdate.UpdateDate;
                serverUnitToUpdate.NameCN = clientUnitToUpdate.NameCN;
                serverUnitToUpdate.NameEN = clientUnitToUpdate.NameEN;
            }
        }
    }

    //获取服务端与客户端的差集用来同步服务端或客户端不存在的实体
    var exceptServer = serverUnits.Except(clientUnits, new UnitEntityCompare());

    //遍历差集集合
    //由于要修改集合,所以不使用foreach
    for (int i = 0; i < exceptServer.Count(); i++) {
        var item = exceptServer.ElementAt(i);
        //如果本地最后更新时间在数据的更新时间之前,则向客户端添加该数据
        if (!clientShopInfo.LastUpdateDate.HasValue || clientShopInfo.LastUpdateDate < serverShopInfo.LastUpdateDate) {
            using (var helper = new LocalDb.UnitHelper()) {
                helper.AddUnit(item);
            }
        }
        //否则说明数据已从本地库删除,同时从服务端数据库删除
        else {
            Context.RP_Units.Remove(item);
        }
    }

    //获取服务端与客户端的差集用来同步服务端或客户端不存在的实体
    var exceptClient = clientUnits.Except(serverUnits, new UnitEntityCompare());

    //遍历差集集合
    //由于要修改集合,所以不使用foreach
    foreach (var item in exceptClient) {
        //如果本地最后更新时间在数据的更新时间之前,则从客户端移除该数据
        if (clientShopInfo.LastUpdateDate < serverShopInfo.LastUpdateDate) {
            using (var helper = new LocalDb.UnitHelper()) {
                helper.DeleteUnit(item);
            }
        }
        //否则说明将客户端数据添加到服务端
        else {
            Context.RP_Units.Add(item);
        }
    }

    SyncOperations.Remove("Unit");
}
#endregion
posted @ 2011-12-24 01:56 紫色永恒 阅读(62) 评论(0) 编辑

Expression Blend 5 Preview Ultimate for Silverlight 5 Release Candidate已经发布。该版本的Blend只支持Silverlight5项目(包括Sketch Flow) 。

更多细节请浏览 Microsoft Download Centre.

下载地址: Download Microsoft Expression Blend 5 Preview for Silverlight 5 RC

posted @ 2011-09-07 17:56 紫色永恒 阅读(758) 评论(0) 编辑

参考资料:The Big List of What’s New or Improved in Silverlight 5

以下总结包括大家关心的Silverlight5的绝大多数新功能或改进,特别是Silverlight5 RC中的新玩意。

数据绑定相关
  • ICustomTypeProvider
  • 自定义标记扩展(Custom Markup Extensions)
  • Ancestor RelativeSource 绑定
  • 隐式 Data Templates
  • 绑定到Style的Setter
  • DataContextChanged 事件
  • UpdateSourceTrigger中新增PropertyChanged
图形/显示相关
  • XNA 3D API
  • 改进Graphics Stack
  • 3D 目标呈现 
  • XNA 3D效果内置
  • 3D 表面构成设置
  • 3D 采样抗锯齿
多媒体
  • 应用XNA SoundEffect及SoundEffectInstace的低延时音效
  • 硬编码H.264
  • 回放及变速
  • 支持遥控器及MediaCommand
文本
  • 文本间隙调节
  • 串联及多列文本
  • 支持OpenType 
  • Pixel Snapped 文本及相应的 TextOptions
OS交互
  • P/Invoke
  • 多窗口
  • 在完全信任模式下无限制的文件通信
  • 浏览器内完全信任模式
  • SaveFileDialog 和 OpenFileDialog 支持默认文件名
  • 支持64位浏览器
  • 当使用Silverlight播放多媒体内容时提供更好的电源管理(如不让计算机进入休眠、屏幕保护等状态)
性能及生产力提升
  • 改进网络延迟
  • 可以对Xaml中的数据绑定进行调试
  • 解析器性能提升
  • 使用多核JIT减少启动时间
控件
  • 支持双击(多击)
  • PivotViewer 控件
  • 在ComboBox中可以根据按键选择到相应的项(比如ComboBox中有Alice、Lily、Tom,当你敲击T建的时候会直接跳到Tom选项上)
其他
  • 浏览器内使用 HTML 控件
  • 矢量打印
  • 融入了TPL(Tasks Parallel Library)并行类库的子集简化异步操作
posted @ 2011-09-06 09:57 紫色永恒 阅读(529) 评论(2) 编辑

原文地址:http://www.silverlightshow.net/items/Working-with-collections-in-WCF-RIA-Services-part-two.aspx

这是本文的第二部分。

在第一部分中,我们讨论了两个相对简单的集合类型:EntitySet和EntityList。在本文中,我们将更进一步的了解其他两个更高级的类型:ICollectionView和DomainCollectionView。

 

ICollectionView

ICollectionView并不是一个新的接口,已经有大量的Silverlight控件对其进行了实现,如DataGrid。现在,我们可以直接在ViewModel中使用它。为了允许控件绑定到一个ICollectionView的实现(如我们熟悉的CollectionViewSource和PagedCollectionView),我们可以这样做:

 

private ICollectionView CreateView(IEnumerable source) {
    CollectionViewSource cvs = new CollectionViewSource();
    cvs.Source = source;
    return cvs.View;
}

private ICollectionView _books;
public ICollectionView Books {
    get {
        if (this._books == null) {
            this._books = CreateView(this.Context.Books);
            this._books.Filter = new Predicate<object>(BookCorrespondsToFilter);
        }
        return this._books;
    }
}

当载入Book数据时会自动反映到View中:

public CollectionViewViewModel() {
    InstantiateCommands();

    // load books
    Context.Load<Book>(Context.GetBooksQuery().Take(10));
}

 

ICollectionView:添加和移除数据

可以通过直接从Context添加和移除实体来完成,这些EntitySet的变化变化都会被CollectionViewSource跟踪到。

那么,这么做的意义到底是什么?目前为止我们还没看到这种方式与直接使用EntitySet的区别是吧?其实,ICollectionView的真正特别之处体现在可以添加过滤、排序和分组规则。

ICollectionView的过滤

ICollectionView定义了一个Predicate<object>类型的Filter属性,让我们略加修改我们的代码:

private ICollectionView _books;
public ICollectionView Books {
    get {
        if (this._books == null) {
            this._books = CreateView(this.Context.Books);
            this._books.Filter = new Predicate<object>(BookCorrespondsToFilter);
        }
        return this._books;
    }
}

public bool BookCorrespondsToFilter(object obj) {
    Book book = obj as Book;

    if (filterActive) {
        return book.Title.Contains("Silverlight");
    }

    return true;
}

BookCorrespondsToFilter方法执行时会检查每一个Book的Title属性是否包含“Silverlight”这个单词,如果不包含,则它不会被显示在View中。

当前代码提供的功能仅当你明确知道过滤时机时使用,然而大部分的应用程序具有要用户自己确定过滤时机的需求,那么我们再来进行一些改动:添加filterActive属性,当用户点击Add Filter时它被置为true。

public bool BookCorrespondsToFilter(object obj) {
    Book book = obj as Book;

    if (filterActive) {
        return book.Title.Contains("Silverlight");
    }

    return true;
}

然当我们点击按钮的时候,我们会发现界面并没有发生任何变化,为什么呢?

当我们针对过滤条件做出改变或Book实体发生变化时(如更改它的书名),ICollectionView的实现不回自动再次执行过滤:Filter方法仅在将实体添加到EntitySet时执行。这意味你不得不明确的通知它使用新的过滤条件重新检查已经载入的实体,我们可以通过调用ICollection的Refresh()方法实现:

Refresh = new RelayCommand(() => {
    Books.Refresh();
});

现在,View会被重新创建,这会让所有的Book实体被重新过滤。当然这仅当我们改变过滤条件或EntitySet发生改变时才是必要的。

 

ICollectionView的排序和分组

ICollectionView具有SortDescriptions和GroupDescription这两个有趣的属性,可以使用它们定义针对EntitySet的排序和分组规则。

排序操作可以通过点击绑定了ICollectionView的DataGrid列头实现,但当我们使用其他的一些诸如ListBox一类没有表头的控件时则需要通过代码的方式改变它们的排序规则:

AddSort = new RelayCommand(() => {
    Books.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
});

对集合的分组操作类似:

AddGrouping = new RelayCommand(() => {
    Books.GroupDescriptions.Add(new PropertyGroupDescription("Author"));
});

效果如图:

clip_image002

有一点要注意,一旦对集合进行分组操作,UI虚拟化将会被关闭-所以在操作大量数据时要谨慎一些。当对大量数据进行分组时一般要和分页相配合。

当我们要进行排序、过滤和分组操作时,ICollection是一个非常好的选择。然而它只能作用于内存中的数据,这意味着所有的数据都必须载入到客户端。这适合大部分的应用场景。而其他的情况我们可以通过DomainCollectionView解决。

 

DomainCollectionView

有很多的企业级应用中会有成千上万甚至百万千万级的数据要进行排序、过滤和分组。面对这类场景,ICollectionView就不再适用了,原因上文已经说明。我们需要一个允许服务端排序、过滤、分组以及更重要的分页操作的集合。

这就是DomainCollectionView的职责,你可以在WCF RIA Services Toolkit中的Microsoft.Windows.Data.DomainServices程序集中找到它(该程序集已经包含在示例代码中)。使用DomainCollectionView需要我们进行相比其他集合更多的设置,不过一旦你掌握了这些设置你会发现它们依然十分简单。DomainCollection初始化时需要Source和Loader(默认是CollectionViewLoader)属性。

public DomainCollectionView<Book> Books
{
    get { 
        return this.view; 
    }
}

Source定义了用于View的源实体(任意的实现了IEnumerable的集合),典型的例子就是实现了INotifyCollectionChanged的集合。

this.source = new EntityList<Book>(Context.Books);

而Loader关注点在于数据的载入。当我们使用默认的CollectionViewLoader时需要同事传入两个回调:OnLoad和OnLoadCompleted,它们分别定义当数据必须被载入和载入操作完成时的发生的事件(当然如果你愿意的话,也可以用一个简单些的LoadOperation代替CollectionViewLoader)。

this.loader = new DomainCollectionViewLoader<Book>(
    this.OnLoadBooks,
    this.OnLoadBooksCompleted);
private LoadOperation<Book> OnLoadBooks()
{
    return this.Context.Load(this.query.SortPageAndCount(this.view));
}

private void OnLoadBooksCompleted(LoadOperation<Book> op)
{
    if (op.HasError)
    {
        op.MarkErrorAsHandled();
    }
    else if (!op.IsCanceled)
    {
        this.source.Source = op.Entities;
        if (op.TotalEntityCount != -1)
        {
            this.Books.SetTotalItemCount(op.TotalEntityCount);
        }
    }
}

正像你所看到的那样,我们在OnLoadeBooks中确认请求执行时包含了排序(SortDescription)和分页(仅当前页所需数据被载入)以及数据总数(DataPager需要用到)。

当Books被载入时,Source集合会被设置成载入的Book实体,并通过TotalEntityCount得到实体总数赋给TotalItemCount属性。

this.view = new DomainCollectionView<Book>(loader, source);

其余的部分用来进行载入的初始化(如设置页大小为5等):

using (this.view.DeferRefresh())
{
    this.view.PageSize = 5;
    this.view.MoveToFirstPage();
}

(使用DeferRefresh()的可以让view的刷新数据事件推迟到所有using包含的代码段执行后再执行)

其实,当我们进行分页、排序(以及其他的可能出发View刷新的操作)时,Loader都被执行并载入数据。一旦载入操作完成便会更新Source属性并通过通知机制让View获知更新同时响应变化。

(注:在WCF RIA Services Toolkit的April版本中,SortPageAndCount已经改成了SortAndPageBy)

 

DomainCollectionView:添加和移除数据

代码如下

AddBook = new RelayCommand(() =>
{
    // you can add books like this, but DCV is server side oriented: to get correct 
    // behaviour, you should add it to the Context and submit the changes, after which
    // the next query will fetch the book you just added.
    Book book = Books.AddNew() as Book;
    book.Author = "Kevin Dockx";
    book.ASIN = "123456";
    book.Title = "Silverlight for dummies";
});

DeleteBook = new RelayCommand(() =>
{
    // deleting an item can be done like this, but should be done directly on the context 
    // & submitted to the server
    Books.RemoveAt(0);
});

然后这样的操作会导致View的筛选、排序等规则不同步,毕竟DomainCollectionView在被设计工作在服务端的。

正确的添加和移除实体的方式是同时在服务端进行相应的操作,如:添加一个实体到Context中(或从Context中移除),调用SubmitChanges提交至服务端然后刷新你的DomainCollectionView。

DomainCollectionView:数据的过滤、排序及分组

接下来我们看一下如何对数据进行过滤。很简单,只要在EntityQuery后面增加相应的Where条件即可,比如:

AddFilter = new RelayCommand(() =>
{
    // filters in DCV should be done by adding a Where clause to the query, as DCV is mainly used for
    // server side logic

    this.query = Context.GetOrderedBooksQuery().Where(b => b.Title.Contains("Silverlight"));
    this.view.MoveToFirstPage();

});

接下来是排序和分组。当我们点击列头时,SortDesscription会被添加到Book集合中决定下次读取数据的排序策略并自动重新获取数据。

有一些应用程序中要求当排序规则变化后列表跳转到第一页。为对应这样的需求,我们则要像这样写一条event handler:

INotifyCollectionChanged notifyingSortDescriptions = (INotifyCollectionChanged)this.Books.SortDescriptions;

notifyingSortDescriptions.CollectionChanged += (sender, e) => {
    this.view.MoveToFirstPage();
};

像使用ICollectionView一样,我们也可以添加使用代码向DomainCollectionView中添加自定义的SortDescription

AddSort = new RelayCommand(() => {
    Books.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
    Books.Refresh();
});

分组的方式类似:

AddGrouping = new RelayCommand(() => {
    Books.GroupDescriptions.Add(new PropertyGroupDescription("Author"));
    Books.Refresh();
});

将以上内容整合一下,我们最后得到了一个服务端分页、排序和分组的集合:

clip_image004

总结

WCF RIA Services SP1新增或增强了许多的集合类型。无论是更好的绑定选项还是服务端可分页、排序的集合都让其与MVVM的交互变得更加便捷。如果你在使用WCF RIA Services配合MVVM开发行业软件或商用程序,那么对这些新的集合类型的了解就显得十分必要了。

posted @ 2011-07-19 09:17 紫色永恒 阅读(1025) 评论(1) 编辑

原文地址:http://www.silverlightshow.net/items/Working-with-collections-in-WCF-RIA-Services-part-one.aspx

本文分为两部分,本篇为第一部分。

介绍

今天,很多的商业应用是使用WCF RIA Services构成的(这并不奇怪,它确实是一个强大的高扩展性框架)。然而它对集合类型的支持可以变得更好。你经常会做这样的操作:获取实体(通常是一个Load Operation)在其完成事件中将其添加到一个ObservableCollection<T>中,现在你依然可以这样做。不过在WCF RIA Services的第一个SP中分别对一些之前就存在的集合类型进行了加强,同事也增加了一些新的集合类型。这些变化使你在使用WCF RIA Services配合MVVM模式时更得心应手。现在我们可用的集合可以自动跟踪你的DomainContext,你可以添加过滤、排序甚至分组条件,还提供了一个服务端可分页的DomainCollectionView。在这篇文章中,我们会一起探讨一下这些增强及新增的新集合类型以及看看它们在哪些场景中使用起来更给力。

本文中将会涉及WCF RIA Services中的四种集合类型:EntitySet(增强)、EntityList(新增)、CollectionView(增强)以及DomainCollectionView(新增)。你可以在这里下载源码

WCF RIA Services SP1包含在Visual Studio 2010 SP1中,点此下载

EntitySet<T>

EntitySet是WCF RIA Services应用程序中的一个基本集合类型—当我们建立ViewModel时会经常用到它。它是一个带有一些用来自定义返回类型选项的无序集合。因此当我们想查看通过DomainContext载入的某个类型的所有实体时,我们会经常使用它。

下图是一个示例程序,我将Books属性绑定到ListBox上。

clip_image002

看下面的代码:

/// <summary>
/// The Books property
/// </summary>
public EntitySet<Book> Books{
    get{
        return Context.Books;
    }
}

像你看到的那样,Books属性是一个Book类型的EntitySet集合,它只是Context.Books的简单引用。在构造函数中,我们载入10条数据:

public EntitySetViewModel() {

    InstantiateCommands();

    // load books
    Context.Load<Book>(Context.GetBooksQuery().Take(10));
}

当需要更多数据时,这样

LoadMoreBooks = new RelayCommand(() => {
    Context.Load<Book>(Context.GetBooksQuery());
});

当更多的Book类型的实体被添加到正确的Context.Books这个EntitySet时,这些新增的Books也被加载到ListBox中。换句话说,当Book数据被DomainContext载入时,它们也被同时添加到ContextBooks这个EntitySet中了。

EntitySet<T>:添加和移除数据

那么如何向EntitySet添加或移除数据呢?下面的代码展示如何添加数据:

AddBook = new RelayCommand(() => {
    Context.Books.Add(new Book() {
        Author = "Kevin Dockx"
        ,
        ASIN = "123456"
        ,
        Title = "Dummy book"
    });
});

然后是移除数据:

DeleteBook = new RelayCommand(() => {
    Context.Books.Remove(Context.Books.FirstOrDefault());
});

当你调用SubmitChanges()方法时,DomainService会将增加/删除的数据切实的反映为服务端的相应操作。

EnityList<T>

下一个要介绍的集合类型是:EntityList<T>,它是一个新增的集合类型,你可以在WCF RIA Services Toolkit中找到它,它位于Microsoft.Windows.Data.DomainServices命名空间下(源码中已经包含了这个程序集)。本质上说它是一个基于EntitySet的Observable集合。它的优点是允许我们取得通过DomainContext载入的特定类型实体数据的子集。定义一个EntityList<T>属性大概是如下的样子:

private EntityList<Book> _books;
public EntityList<Book> Books {
    get {
        if (this._books == null) {
            this._books = new EntityList<Book>(
                this.Context.Books);
        }
        return this._books;
    }
}

Entity List有一个Source属性,它定义了EntityList应该包含的实体:

public EntityListViewModel() {
    InstantiateCommands();

    // load books
    this.Books.Source = Context.Load<Book>(Context.GetBooksQuery().Take(10)).Entities;
}

通过这两段代码,你的EntityList就已经初始化并准备好了。

一旦你开始读取更多的Book类型实体,事情就变得有趣起来了:数据变化不会自动的反映到你的EntityList中。如果你想让EntityList对那些新增的Book作出反应,则需要设置它的Source属性:

LoadMoreBooks = new RelayCommand(() => {
    this.Books.Source = Context.Load<Book>(Context.GetBooksQuery()).Entities;
});

EntityList<T>:添加和移除数据

添加一条新的Book实体通常通过两种方式,直接添加到Context中或添加到EntityList本身。这两种方式需要使用不同的方法处理。可以看到我们示例中的代码:

AddBook = new RelayCommand(() => {
    Context.Books.Add(new Book() {
        Author = "Kevin Dockx"
        ,
        ASIN = "123456"
        ,
        Title = "Dummy book"
    });
});

一条新的Book被添加到Context中。不过EntityList不会自动的获知这些变化(上文说过,实现项的子集的变化跟踪需要通过设置Source属性,而非每次Context的载入变化),ListBox仍然显示EntityList的Source属性中的实体数据集合。

当如下代码执行时:

AddBookToEntityList = new RelayCommand(() => {
    Books.Add(new Book() {
        Author = "Kevin Dockx"
        ,
        ASIN = "123456"
        ,
        Title = "Dummy book"
    });
});

一条新的Book实体被添加到EntityList中,这将直接反映到我们的ListBox控件中。如果EntitySet中不存在这条Book实体,则它会被同时添加到其对应的EntitySet中。

而移除一条Book的行为又有一些不同:当你从Context的EntitySet中移除Book数据的时候,它会直接反映到EntityList中:

DeleteBook = new RelayCommand(() => {
    Context.Books.Remove(Context.Books.FirstOrDefault());
});

当你从EntityList中移除一条Book时,EntitySet中也会同时移除这条实体。

DeleteBookFromEntityList = new RelayCommand(() => {
    Books.Remove(Books.FirstOrDefault());
});

这是本文的第一部分,第二部分将会对更高级的集合类型进行介绍,他们是:ICollectionView和DomainCollectionView,敬请期待。

posted @ 2011-07-15 16:05 紫色永恒 阅读(1122) 评论(8) 编辑
摘要: 由于本示例基于MVVMLightToolkit,所以我们的ViewModel基类继承自MVVMLightToolkit提供的ViewModelBase,并命名为AdvancedViewModelBase,项目中所有的ViewModel都继承自这个类,先看类图: 由于本示例基于MVVMLightToolkit,所以我们的ViewModel基类继承自MVVMLightToolkit提供的ViewMod...阅读全文
posted @ 2011-06-28 10:13 紫色永恒 阅读(1369) 评论(7) 编辑
摘要: WCF Service(WebService) Web Services是经实践考验证明的跨防火墙的通信方式,它很稳定且被广泛认可。总的来说你需要为分散的CRUD操作指定相应的接口并在Silverlight中忠实的调用他们 使用的原因:需要进行类似直接通过服务进行数据库交互操作的项目(弱化业务逻辑部分)。 避免使用的原因:必须始终自己监视数据的变化并调用相应的服务方法进行更新,任何需要并发的操作或...阅读全文
posted @ 2011-06-23 17:13 紫色永恒 阅读(1673) 评论(6) 编辑
摘要: 距离Silverlight5(beta)华丽丽的亮相已经有一段日子了,园子里也有同道陆续放出了beta版中新玩意的体验和介绍,比如乱世经典的这些文章,估计大家早也已经对beta版sl5带来的新特性了然于胸了。 而这篇文章的目的则是要向大家介绍一下那些没有赶上beta版末班车却又让我们“魂牵梦绕”的sl5新特性。废话少说,且听我一一道来。 DataContextChanged事件 顾名思义,当Dat...阅读全文
posted @ 2011-04-26 17:14 紫色永恒 阅读(1372) 评论(5) 编辑
摘要: 生活情况:从兰州机场出来我们都傻了,遍地的土黄色的山。从兰州机场坐大客到火车站的一路都不知道是什么感觉了。在火车站幸运的买到最后两张当天的卧铺票第二天早上10点多到达敦煌。在火车上看到的景色就更别提了,沙漠,骆驼草什么的。经过世界最长隧道(23公里)的时候出现轻微高原反映,第二天就好多了。 敦煌火车站很气派,不过这个好像是世界上距离市区最远的火车站,从火车站打车到达目的地除了一些葡萄架和奇奇怪怪的枯树外神马都没看到。 敦煌古镇咱们是没看到,据说繁华的地方和沈阳差不多,不繁华的地方那也不用说了。我们这地方是七里镇,基本都是油田的人。被安排在处理井下业务的一个办公楼的一楼。条件还算可以,不过缺了不阅读全文
posted @ 2011-02-18 15:32 紫色永恒 阅读(160) 评论(0) 编辑
摘要:     像大多数的项目一样,即使在将Silverlight应用程序交付后,我们仍会根据客户的需求进行相应修改并将xap文件发送给客户,客户经过测试后再部署到生产环境。     这样经常会面临一个问题,客户更新xap后并未看到任何变化甚至由于我们wcf服务等相关文件的改动会让程序执行发生异常。这个原因倒是很明显--我们的xap文件被缓存了,当服务器上替换了新的xap文件时,本地并未重新从服务器上下...阅读全文
posted @ 2011-01-07 10:12 紫色永恒 阅读(754) 评论(7) 编辑