当前位置: 首页 > 编程日记 > 正文

Catel(翻译)-为什么选择Catel

1. 介绍

这篇文章主要是为了说明,我们为什么要使用Catel框架作为开发WPF,Silverlight,和Windows phone7应用程序的开发框架。

2. 通用功能

2.1. 这是你的选择

针对需对开发者,再使用架构的时候是希望有很大的自由度的,但是大部分框架则需要开发者完全遵循他的规则,要么都使用这个框架,要么就不能使用。而catel不是,Catel包含很多的功能:日志,诊断,反射,MVVM,用户控件,窗体,这些所有的功能都是与其他功能互补的,二针对Catel则是你可以决定使用这些功能中的一个,或多个,或全部。而对于大部分应用程序都会有一个启动项-bootstrapper,通过这个来决定你的架构,当这个决定后,好多东西都是定好的,比如说Views的名字必须这样,控件的名字必须那样,而Catel可以让你自由设置。还有一点最重要Catel可以和其他框架一起使用,这样开发者可以很好的利用其他框架来补充应用程序的每个方面来完成一个最好的框架。

2.2  Data handing

有一件很重要的事情是大部分开发者来写用了很多精力来实现对象的序列号。序列号是一种专业的技术,尽管少数的开发者能够掌握序列化方法(考虑程序集版本的改变,类的改变(增加或减少属性)等等).而大部分开发者所认为的,掌握的序列号技术,只是创建一个BinaryFormatter对象,如下面代码所示:

BinaryFormatter serializer = new BinaryFormatter();
var myObject = (MyObject)serializer.Deserialize(stream);

大部分开发者都不知道当出现如下情况是反射会出现问题:

  • 1,你更改了assembly的版本
  • 2,你增加或删除了一个属性
  • 3,你增加或删除了一个事件
  • 甚至在你已经掌握了一些知识准备去处理这些问题,和每个开放者一样,我也遇到过这种情况,也是先过代码来处理,我实现了一个DataObjectBase类,这个能够用于作为所有数据类的基类存放在内存中,也可以序列化到磁盘上(比如stean,或者XML或者….)

2.2.1 DataObjectBase 类

使用这个类是非常简单的,你只需要定义一个新的类,继承与DataObjectBase就可以了。

/// <summary>
/// MyObject Data object class which fully supports serialization,
///          property changed notifications,
/// backwards compatibility and error checking.
/// </summary>
#if !SILVERLIGHT
[Serializable]
#endif
public class MyObject : DataObjectBase<MyObject>
{/// <summary>/// Initializes a new object from scratch./// </summary>public MyObject() { }#if !SILVERLIGHT 
/// <summary>
/// Initializes a new object based on <see cref="SerializationInfo"/>.
/// </summary>
/// <param name="info"><see cref="SerializationInfo"/>
//     that contains the information.</param>
/// <param name="context"><see cref="StreamingContext"/>.</param>
protected MyObject(SerializationInfo info, StreamingContext context): base(info, context) { }
#endif
}

正如你在上面代码中所示,MyObject类继承于DataObjetBase,提供一个空的钩子函数,但也有一个构造函数用于二进制序列号,上面的代码看起来复杂,但是可以由一个dataobject代码段来构造,你只需要写类的名字。

2.2.2 定义属性

为类定义属性是非常简单的,属性与dependcy 属性像是,使用这种方式来定义属性的优点如下:

  1. 1,这样定义属性能自动包括序列化,不需要制定复杂的数据接口
  2. 2在当类还没有被构造或者属性没有在序列号中赋值时,你可以为属性指定默认值(这种情况下,属性被加到一个存在的类中)
  3. 3,propertyData对象能够用于获取属性,以至于编译器来检查错误
  4. 4,你可以直接订阅来更改通知,所有的属性都支持了 NodifyPropertyChanged
  5. 如下的代码定义了一个string类型的Name属性。
/// <summary>
/// Gets or sets the name.
/// </summary>
public string Name
{get { return GetValue<string>(NameProperty); }set { SetValue(NameProperty, value); }
}/// <summary>
/// Register the Name property so it is known in the class.
/// </summary>
public static readonly PropertyData NameProperty = RegisterProperty("Name", typeof(string), string.Empty);
  • 如果需要一个注册的属性可以再序列化的时候排除,当对象反序列化时,默认值将可以使用。

2.2.3 序列化

我已经提到序列化好多次了,让我看来看看如何简单序列化你的程序,而不考虑assembly版本,首先,将原来继承于DataObjectBase的对象,改为继承SavableDataObjectBase.依赖目标框架,许多属性作为序列化模式

  1. 1,二进制
  2. 2,XML
  3. 3,DataContract
  4. 4,JSON

下面的代码显示了如何存储一个对象(如果可以,也可以存储一个嵌套的对象)

var myObject = new MyObject();
myObject.Save(@"C:\myobject.dob");

看起来非常的简单,但是这确实是你唯一需要做的事情,你也能够通过重新调用重载的方法,来指定序列化模式。

载入也是很简单的,你可以通过如下的代码

var myObject = MyObject.Load(@"C:\myobject.dob");

2.2.4 提供的功能

DataObjectBase提供了一些功能,如下

  • INotifyPropertyChanged
  • 所有通过RegisterProperty注册的属性都会关注通知信息。

IDataError

非常容易设置字段或业务的错误,通过SetFieldError和SetBussinessError方法,这些方法能够通过改写ValidateFiled和ValidateBussinessRules的方法实现

IEditableObject

数据对象能够自动创建一个内部备份和存储它,如果需要,使用IEditableobject接口

序列化

如说过好多次的,使用SavableDataObjectBase对象,你能够存储你的文件到流(如在disk中,stream在内存中等等)注意这个类并不适合数据库通讯,比较好的办法是转换他(ORM 匹配的方式,通过Entity Framework,NHibernate,LLBLGen Pro 等等)

这个API是位于Catel.Core程序集中,这个可以不只使用WPF(也可以用于ASP.NET,Windows Forms)等等。

2.3 MVVM基础

在近几年,MVVM已经变成写WPF,Sliverlight和Windows Phone应用程序的一种常用模式,模式本身是非常简单的,但MVVM本身有许多的缺陷很问题:

1,如何在View-Model中显示模型对话框或者是消息框。

2,如何在View-Model中执行进程

3,如何让用户在View-Model中选择一个文件。

在我看来,有许多好的架构将其划分出来,例如,人们直接在View-Model中调用MessageBox.show,如果你也是使用这种方式,问问你自己:谁会在一个单元测试中点击按钮。

在我们真正开始用Catel进行开发前,我们需要通过调查来确定MVVM模式在Line of Business(LoB)是真正有用的。基于这个调整和研究,我们创建了一个稳固的MVVM模型来解决MVVM模式中已知的这些问题 。

2.3.1 ViewModelBase

如大多数MVVM框架一样,所有View-Models都使用ViewModelBase作为基类,这个基类继承与DataObjectBase类(在文章的前门提到过这个类),这个将会有如下的优点:

  • 1,类似于Dependency属性的方式
  • 2,自动监控更改
  • 3,支持字段和业务错误
  • 因为当前类继承与DataObjectBase,你可以增加字段或者是业务错误,这些将自动反应到UI上么,编写View-Models是如此的简单。
  • 2.3.2 Model 映射
  • 在使用MVVM模式时,我们注意到,很多的开发者都有一个Model,并且将这个Model的属性匹配到View-Model上,当UI关闭后,用户又将这些属性重新放回到Model中, 在使用Catel的时候,则不需要实现这些代码。
  • 在Catel中,我们创建特性,运行你定义属性作为一个Model,Model是作为View-Model表现效果的一个部分呈现给用户的。View-Model可以实现多个模型的组合。
  • 顶一个模型是非常简单的,你只需要定义Model中的特性.

/// <summary>
/// Gets or sets the shop.
/// </summary>
[Model]
public IShop Shop
{get { return GetValue<IShop>(ShopProperty); }private set { SetValue(ShopProperty, value); }
}/// <summary>
/// Register the Shop property so it is known in the class.
/// </summary>
public static readonly PropertyData ShopProperty = RegisterProperty("Shop", typeof(IShop));

使用Model特性是非常强大的,基本上这是在View-Model中的扩展,如果模型支持IEditableObject,BaseEdit将自动在View-Model的Initialize中被调用。当前View-Model被Cancel,则CancelEdit也会被调用保证更改被回退掉。而当模型被定义时,也可以使用ViewToModel特性,如你下面的代码所看到的

/// <summary>
/// Gets or sets the name of the shop.
/// </summary>
[ViewModelToModel("Shop")]
public string Name
{get { return GetValue<string>(NameProperty); }set { SetValue(NameProperty, value); }
}/// <summary>
/// Register the Name property so it is known in the class.
/// </summary>
public static readonly PropertyData NameProperty = RegisterProperty("Name", typeof(string));

在上面代码中的ViewModel特性会自动匹配View-Model 中的Name属性到Shop.Name属性中,使用这种方式,你不需要手工去编写这些代码来从模型中获取值,另一个很好的效果是View-Model将自动验证所有定义在Model特性中的对象,所有的字段和业务的错误将自动匹配到View-Model上

总的来说,Model和ViewModelToModel特性将确定不需要重复验证或者人工匹配。

2.2.3 Commands

Commanding 在Catel能被很好的支持,Catel支持Command类,这些在其他框架中被称之为RelayCommand或者是DelegateCommand,在View-Model中定义一个Command是已经非常容易的时候,你可以在下面的代码中看到。

// TODO: Move code below to constructor
Edit = new Command<object, object>(OnEditExecute, OnEditCanExecute);
// TODO: Move code above to constructor/// <summary>
/// Gets the Edit command.
/// </summary>
public Command<object, object> Edit { get; private set; }/// <summary>
/// Method to check whether the Edit command can be executed.
/// </summary>
/// <param name="parameter">The parameter of the command.</param>
private bool OnEditCanExecute(object parameter)
{return true;
}/// <summary>
/// Method to invoke when the Edit command is executed.
/// </summary>
/// <param name="parameter">The parameter of the command.</param>
private void OnEditExecute(object parameter)
{// TODO: Handle command logic here
}

有一些人并不喜欢ICommand实现,例如Caliburn(Micro)使用约定,而并不需要创建Command,这种做法有些缺陷。如下

  • 1,这个需要你确保控件名与方法名一致
  • 2,如果你不是完全熟悉约定,你将不好分辨出是否是一个Command.
  • 3,这个方法是一个Public(否则,你将在UnitTest中无法调用Commands),这些看起来很自由,但其实并不是我们所看到的那样。
  • 4,你将在Excute中一直调用CanExecute,因为你不能保证Excute在约束匹配的时候就执行。
  • 5,没有办法人工的刷新CanExcute状态

2.3.4 Services

服务室Catel解决需要将消息传递给用户的问题,让用户开始一个进程,ViewModelBase有一个GetServcie方法来通过一个IOC容器获得获得实际实现的一个接口,对于WPF和Silverlight,Catel使用Unity,对于Windows Phone7,IOC是通过定制实现的。

在Catel中,服务最重要的效果是,默认情况下,真正的实现是使用Unity容器注册过的,通过Ioc容器,你可以注册测试实现,例如,MessageBox  Click实际,在这种情况下,你能够模仿用户点击OK,如果你想,你也可以模仿Cancel,这样你可以通过单元测试用户在View-Model中执行的所有可能的操作。

使用services时非常容易的,如前面所提到的,GetServcie方法获取service的真正执行,例如,显示一个消息给用户,你可以使用如下的代码

var messageService = GetService<IMessageService>();
messageService.ShowError("An error occurred");

所有的服务都能被简单的使用,Catel支持一些services,如下是services能够支持的目标框架

服务名称和描述WPFSliverlightWP7
ILogger -确保写消息日志Description: C:\Users\Geert\Desktop\yes.pngDescription: C:\Users\Geert\Desktop\yes.pngDescription: C:\Users\Geert\Desktop\yes.png
ILocationService -返回地理位置Description: C:\Users\Geert\Desktop\no.pngDescription: C:\Users\Geert\Desktop\no.pngDescription: C:\Users\Geert\Desktop\yes.png
IMessageService -显示弹出框Description: C:\Users\Geert\Desktop\yes.pngDescription: C:\Users\Geert\Desktop\yes.pngDescription: C:\Users\Geert\Desktop\yes.png
INavigationService - 导航服务Description: C:\Users\Geert\Desktop\no.pngDescription: C:\Users\Geert\Desktop\no.pngDescription: C:\Users\Geert\Desktop\yes.png
IOpenFileService -让用户选择文件并打开Description: C:\Users\Geert\Desktop\yes.pngDescription: C:\Users\Geert\Desktop\no.pngDescription: C:\Users\Geert\Desktop\no.png
IPleaseWaitService – 在UI线程上启动一个等待标记Description: C:\Users\Geert\Desktop\yes.pngDescription: C:\Users\Geert\Desktop\yes.pngDescription: C:\Users\Geert\Desktop\no.png
IProcessService - 执行进程Description: C:\Users\Geert\Desktop\yes.pngDescription: C:\Users\Geert\Desktop\no.pngDescription: C:\Users\Geert\Desktop\no.png
ISaveFileService - 让用户选择一个文件来保存Description: C:\Users\Geert\Desktop\yes.pngDescription: C:\Users\Geert\Desktop\no.pngDescription: C:\Users\Geert\Desktop\no.png
IUIVisualizerService - 显示模态窗口Description: C:\Users\Geert\Desktop\yes.pngDescription: C:\Users\Geert\Desktop\yes.pngDescription: C:\Users\Geert\Desktop\no.png

当然你也可以设计你自己的服务

2.3.5与其他View-Models交互

大部分的框架需要设置复杂的消息系统或者其他的技术来在View-Models直接进行通信,这个方法的缺点在于一旦View-Model是指在ModuleX中实现,并且你关注View-Model,ModelX的开发者必须关注其他的ViewModel,而现在你唯一需要做的事情是设置一个InterestedIn特性,如下代码所示。

[InterestedIn(typeof(FamilyViewModel))]
public class PersonViewModel : ViewModelBase

那么,在PersionViewModel中(这个关注FamiliyViewModel的更改),你只则只需要修改OnViewModelPropertyChanged事件方法,即可。

/// <summary>
/// Called when a property has changed for a view model type
/// that the current view model is interested in. This can
/// be accomplished by decorating the view model with the <see cref="InterestedInAttribute"/>.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="propertyName">Name of the property.</param>
protected override void OnViewModelPropertyChanged(IViewModel viewModel, string propertyName)
{// You can now do something with the changed property
}

也有可能关注多个View-Models,从View-Model被传递到OnViewModelPropertyChanged中,非常容易检查View-Model类型。

2.3.6 嵌套用户控件问题

在MVVM中,有一个复杂架构问题,称之为”嵌套控件问题“,直到现在,我们没有发现其他框架能够使用一个清晰的方式来解决这个问题。这个问题是嵌套的控件应该很容易获取自己所在的View-Model,但是,如何去管理这个,我们看到了许多的解决方案,比如:

1,在另一个View-Model中定义嵌套的View-Models.

2,在最上层的View-Model上定义嵌套用户控件,在用户控件中显示这些属性

下面是这个问题的图片显示

通常的MVVM格式

image005.png

Catel中的MVVM

image006.png

如上面的图片所示,Catel解决这个问题的方法是更加的专业,这个有如下理由说明:

1,分割关注点(每个控件都只有一个View-Model来包含它自己的信息,而并不关系其子节点)

2,用户控件可以被重用

UserControl<TViewModel)能够基于一个实际的用户控件的datacontext来构造一个ViewModel,例如,当你定义了嵌套的用户控件,你唯一需要做的事情就是确认用户控件的datacontext要有一个对象被注入到属于用户控件的View-Model中。

例如,我们有一个persion控件,这个persion控件,只能使用一个有效的IPerson实例(View-Model仅有的构造函数),然后将以如下的方式定义在XAML中。

<Controls:PersonControl DataContext=”{Binding Person}” />

用户控件将关注datacontext的改变,并且尝试去使用正确的datacontext来构建PersonViewModel,在我们的例子中,IPerson接口的一个实例,将自动将IPersion对象转换成PersionViewModel并且控件将有自己的View-Model.

2.4 用户接口

我前面已经提到过很多次-Catel比其他的MVVM模式,提供了更多的UI Elemenet,下面将是最受欢迎的几个。

2.4.1. InfoBarMessageControl

image007.png

InfoBarMessageControl能够通过IDataWariningInfo和IDataErrorInfo接口显示警告和错误给用户,这个控件在以统一的方式显示当前窗体或控件的当前状态给用户的功能上是非常有用的。

2.4.2 DataWindow

当使用WPF开发时,我们一直需要如下的窗体:

1,确定/取消 按钮的数据窗体

2,确定/取消/接受按钮的应用程序设置/选项

3,在一个窗体上的关闭按钮

创建这些窗体的步骤一直是相同的:

1,先在窗体的底部创建一个WrapPanel

2,一边又一遍使用相同的RoutedUICommand对象增加按钮。

DataWindow类使得非常容易的去创建一个基本窗体,简单的给窗体指定一个模式,通过使用这个窗体,你能够将精力集中在实际业务的实现,而不需要担心按钮本身的实现,这个可以节省你的实际,在如下的例子中,是在XAML中编写XMAL到实际的输出控件上。

image008.png

2.4.3 PleaseWaitWindow

image009.png

PleaseWaitWindow是一个在长时间操作中显示等待的窗体,这个也有一个PleaseWaitHelper的类让你更加容易的使用PleaseWaitWindow.

2.5 IO

Catel也针对IO提供了很多扩展API,可能你会问为什么,下面就是显示IO框架

1,支持文件盒文件夹名超过255个字符

2,Combine文件路径或者URL的,比如

Path.Combine(“C:”, “Windows”, “Temp”);.

这些API贴近于System.IO的设计,以便于你很自然地使用它

这个API在Catel.Core程序集中,你可以使用在除WPF以外的地方

2.6 反射

在内部,Catel使用了很多反射技术来实现功能,这章将解释这些反射实现的优点,并不是全部替换了.NET本身实现的反射类,Catel反射类是对默认情况的一个扩展,这个程序及扩展更多的是针对Catel不同的扩展框架来做的一个相同的行为,例如,在WPF中,我们能够使用当前的AppDomain来载入程序集,然而,在Silverlight中,则需要我们来查询当前的Deployment对象来处理,在WP7中,则很难获得载入对象,通过一个独立的类来实现,通过这种方式对所有的不同框架提供统一的接口

这个API在Catel.Core程序集中,你可以使用在除WPF以外的地方

2.7 诊断和日志

Catel使用log4net来作为日志处理的基础,它也提供了额外的功能呢和扩展方法来确保很容易的去记录异常和通用信息,因为Sliverlight和WP7并没有日志能力(客户端),ILog实现一个虚拟的接口。

这个API在Catel.Core程序集中,你可以使用在除WPF以外的地方

2.8 性能

每一个框架都被要求有比较好的性能,我们需要比他们更好,因此,有450个单元测试时用来确保Catel的性能和同步的。这些单元测试在WPF和Sliverlight之间共享,来确保这些行为时一样的,通过这种方式,我们尝试确保在Silverlight在相同的框架下没有性能损失。

3,当前框架的特定功能

如你现在所知道的,Catel支持Sliverlight实现,sliverlight是一个非常受欢迎的平台,但是他缺少已经在WPF中有效的特定,然后使用Catel的开发者,我们尝试在Catel让他们都起到效果。

我们也尝试在Windows Phone 7上实现相同的方式,但是有些不同的东西针对WP7开发,这个是由于不同的UI和状态管理决定的。

3.1 在sliverlight中的自动刷新命令

In Silverlight, commands are not re-evaluated automatically because there is no CommandManager that calls theCanExecute on every routed event. If the user solves an error in the window by, for example, adding a value in an empty field, the CanExecute state of the OK or Save command is not updated (and still is disabled). Other frameworks require the developer to manually refresh the commands.

Catel offers a clean way of providing the same behavior of WPF. Thanks to the ViewModelBase propertyInvalidateCommandsOnPropertyChanged (which is enabled by default for Silverlight and Windows Phone 7), all the ICommand implementations on the View-Model are automatically re-evaluated for you. This way, the user will immediately see the OK or Save button become enabled after setting the value.

3.2.在Sliverlight和WP7中序列化和存储数据

When developing software for Silverlight or Windows Phone 7, you have access to isolated storage.SavableDataObjectBase supports saving and loading of complex graph objects to isolated storage out of the box. To save an object (including all the child objects), you can use the code below:

using (var isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
{using (var isolatedStorageFileStream = isolatedStorageFile.OpenFile("UserData.dob", FileMode.Create, FileAccess.ReadWrite)){myObject.Save(isolatedStorageFileStream);}
}
To load the data again, you can use the following code:
using (var isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
{if (isolatedStorageFile.FileExists("UserData.dob")){using (var isolatedStorageFileStream = isolatedStorageFile.OpenFile("UserData.dob", FileMode.Open, FileAccess.Read)){return MyObject.Load(isolatedStorageFileStream);}}
}

3.3在WP7中的导航

Navigation on Windows Phone 7 is very important. The navigation works the same like the web, thus with request variables. In Catel, it is very easy to navigate to another View (Model) using INavigationService.

To navigate to another View directly, simply use this code inside your View-Model:

var navigationService = GetService<INavigationService>();
navigationService.Navigate("/UI/Pages/MyPage.xaml");
Of course, you can also pass parameters without a hassle:
var parameters = new Dictionary<string, object>();
parameters.Add("MyObjectID", 1);var navigationService = GetService<INavigationService>();
navigationService.Navigate("/UI/Pages/MyPage.xaml", parameters);

The navigation service automatically converts this into the following URL:

/UI/Pages/MyPage.xaml?MyObjectID=1

3.4 Retrieving and simulating geographical locations on Windows Phone 7

The Windows Phone 7 device has a great feature: it can retrieve the current geographical location via GPS. However, how can we implement this correctly in MVVM? In Catel, a separate service is available to get the geographical location. The usage is very simple:

var locationService = GetService&lt;ILocationService&gt;();
locationService.LocationChanged += OnCurrentLocationChanged;
locationService.Start();

It is very important to stop the service when you no longer need it (think about the battery of the user):

var locationService = GetService&lt;ILocationService&gt;();
locationService.LocationChanged -= OnCurrentLocationChanged;
locationService.Stop();

In the OnCurrentLocationChanged event, you can simply query the location from the EventArgs:

private void OnCurrentLocationChanged(object sender, LocationChangedEventArgs e)
{if (e.Location != null){MapCenter = new GeoCoordinate(e.Location.Latitude, e.Location.Longitude, e.Location.Altitude);}
}

4,使用MVVM来写一个N层架构

我们收到了许多用户的关于MVVM的如何实现一个N层架构的问题,大部分的用户认为MVVM来替代业务层,但并不是这种情况,这个章节将解释如何使用MVVM来写一个LOB应用程序的可能,这个将是一个简单或者是复杂的你所想要到东东。

依赖于终端用户的复杂性的需求,你可能决定在你的应用程序中使用一个多层架构,我并不相信一个单一的标准,但是层的一个实际方案决定了每个项目不能被超越创建??,但是没有层将被遗忘。

这章中以图片的形式显示N层架构,MVVM如何被使用在架构中,很久之前,N层架构是被以一种关注点分离的方式来开发的,近来,数据访问层(DAL)本身是一个复杂的东西,现在,有了许多的ORM技术来为你生成DAL,一些ORM技术,如LLBLGen Pro,允许你在DAL中增加你自己的验证和DAL与业务功能合起来。

4.1 2层应用系统

写两层架构师非常简单的,非常简单的应用程序并不需要任何强类型或者是业务规则,下面是2层架构使用MVVM的展示图。

image011.png

正如你所看到的,使用3层架构的是DAL并没有参与到MVVM中,业务逻辑层使用DTO对象作为模型,并且将DTO对象转换成DAL能够处理的Entties,我们看到许多用户从UI层开始引用DAL,但是这个是错误的,下面是3层架构中显示的好的或坏的处理方法。

image012.png

图中的箭头表示使用。

好的解决方案

     在好的解决方案中,我们看到UI层使用BL层,然后BL层使用DAL层,上一次能一直使用一个较低的”down-the-hill”,下面的层应该不要使用上面的层(在非常错误的解决方案中有显示)

因为DAL并没有被引用,所以需要创建DTO对象来在DAL和BL直接传递数据。DTO在UI层也是有效的。

错误的解决方案

错误的解决方案显示,我们看到开发者直接使用了UI层来访问了DAL,这种方案中,他们不需要创造Custom DTO对象。

如果你想写Custom DTO对象,唯一的选择你需要写个横断关注,可以被所有的层使用,横断层中,你可以针对所有的DAL的实体编写接口,并且在UI和BL层中使用它。

现在,大部分的人们使用ORM映射方式,ORM映射可以使用生成DAL代码,你不需要花费时间自己写,ORM模板是你的选择,你可以生成DTO对象。

非常错误的解决方案

非常错误的解决方案显示用户底层使用了上层的方法,这个事非常错误的,破坏了整个的关注点分离的原则,如果你认为这正确地,那么请你回答,你的BL和UI在WPF中,那么你如何使用相同的BL到WP7或者是ASP.NET中。

4.3 MVVM如何与RIA服务和Silverlight结合

大部分的开发者发现很难去找到一个好的方式使用MVVM结合到Silverlight和RIA Service上,从过一个架构的观点来看,RIA服务只是起到DAO的效果,然后Sliverlight中的RIA服务必须使用相同的办法在DTO中。

转载于:https://www.cnblogs.com/kylix1981/p/3931523.html

相关文章:

iOS 三种类型的Block

Block 的copy 操作 Block 其实来讲有三种类型 全局块 NSConcreteGlobalBlock 栈块 NSConcreteStackBlock 堆块 NSConcreteMallocBlock 全局块存储在全局内存中&#xff0c;相当于单例 栈块存于栈内存中&#xff0c;超出其作用域则马上进行销毁 堆块存在于堆内存中&#x…

2.4G高频PCB天线设计

2.4G高频PCB天线设计转载于:https://www.cnblogs.com/LittleTiger/p/6215262.html

如何一起破解图形化Python调试器

15分钟内从零调试 (Zero-to-Debugging in 15 mins) You don’t realize the value of a debugger until you’re stuck working on a hard-to-visualize problem. But once you fire up a development environment with decent debugging capabilities, you’ll never look bac…

python 之路,Day11 (下)- sqlalchemy ORM

python 之路&#xff0c;Day11 - sqlalchemy ORM 本节内容 ORM介绍sqlalchemy安装sqlalchemy基本使用多外键关联多对多关系表结构设计作业1. ORM介绍 orm英文全称object relational mapping,就是对象映射关系程序&#xff0c;简单来说我们类似python这种面向对象的程序来说一切…

iOS事件响应链

1 如下 NSObject 显然是基类&#xff0c;都是继承与UIResponder. 可以看出UIApplication&#xff0c;UIView&#xff0c;UIViewController都是继承自UIResponder类&#xff0c;可以响应和处理事件 我们都是通过UIResonder 来查找控件的父视图控件。’ 发生触摸事件之后&…

论5级流水32bit risc cpu设计

前段时间用verilog写了一个32bit的risc cpu,五级流水&#xff0c;下板调试已经完全可用&#xff0c;准备后期加入浮点运算器&#xff0c;因为最近事情超级多&#xff0c;因此暂时先把RTL图传上来供大家参考&#xff0c;后面我会讲具体怎么设计。希望大家多多关注 :)转载于:http…

开源项目贡献者_嘿新手开源贡献者:请写博客。

开源项目贡献者by Shubheksha通过Shubheksha 嘿新手开源贡献者&#xff1a;请写博客。 (Hey newbie open source contributors: please blog more.) As a newbie open source contributor, I often felt lost and dejected. I couldn’t figure out how different modules fit…

instanceof, isinstance,isAssignableFrom的区别

instanceof运算符 只被用于对象引用变量&#xff0c;检查左边的被测试对象 是不是 右边类或接口的 实例化。如果被测对象是null值&#xff0c;则测试结果总是false。 形象地&#xff1a;自身实例或子类实例 instanceof 自身类 返回true 例&#xff1a; String snew String(&qu…

POJ - 3538 - Domestic Networks

先上题目&#xff1a; Domestic NetworksTime Limit: 2000MS Memory Limit: 65536KTotal Submissions: 732 Accepted: 204 Special JudgeDescription Alex is a system administrator of Domestic Networks Inc. His network connects apartments and spans over multiple buil…

iOS HitTest 机制

当用户触摸&#xff08;Touch&#xff09;屏幕进行交互时&#xff0c;系统首先要找到响应者&#xff08;Responder&#xff09;。系统检测到手指触摸&#xff08;Touch&#xff09;操作的时候&#xff0c;将Touch 以UIEvent 的方式加入到UIApplication 事件队列中去。UIApplica…

巨石加密_缓解巨石

巨石加密by Ian Belcher伊恩贝尔彻(Ian Belcher) 我们如何将技术堆栈转向基于服务&#xff0c;以开发人员体验为中心的设计 (How we pivoted our tech stack to a service-based, developer experience-focused design) This article documents the problems we experienced w…

Python函数中的参数(一)

函数传递参数时的简要关键点&#xff1a; 1、参数的传递是通过自动将对象赋值给本地变量名来实现的。函数参数在实际中只是Python赋值的一个实例。因为引用是以指针的形式实现的&#xff0c;所有的参数实际上都是通过指针进行传递的。 2、在函数内部的参数名的赋值不会影响调用…

LLDB 调试相关

LLDB 初始 LLDB 是一个有着 REPL 的特性和 C ,Python 插件的开源调试器。LLDB 绑定在 Xcode 内部&#xff0c;存在于主窗口底部的控制台中。调试器允许你在程序运行的特定时暂停它&#xff0c;你可以查看变量的值&#xff0c;执行自定的指令&#xff0c;并且按照你所认为合适的…

javascript优缺点_为什么要在JavaScript中使用静态类型? 优缺点

javascript优缺点by Preethi Kasireddy通过Preethi Kasireddy 为什么要在JavaScript中使用静态类型&#xff1f; 优缺点 (Why use static types in JavaScript? The Advantages and Disadvantages) We covered a lot of ground in Part 1! With syntax out of the way, let’…

大数的减法函数--c语言

代码展示&#xff1a; http://paste.ubuntu.com/23693598/ #include<stdio.h> #include<stdlib.h> #include<string.h> char * largeDiffer(char *a,char *b){ /* 使用说明 传入的a和b只能为整数 结果为a-b;返回的为字符指针&#xff0c;注意数组不要越…

json 基础

json格式 JSON格式&#xff1a;http://www.json.org/ python和JSON的关系请参考&#xff1a;http://docs.python.org/library/json.html JSON建构有两种结构&#xff1a; 1. “名称/值”对的集合&#xff08;A collection of name/value pairs&#xff09;。不同的语言中&#…

iOS 中 load 和 initialize的实现顺序

1 load 函数 调用时机&#xff0c;当类引用进项目的时候执行load函数&#xff0c;在main函数开始之前&#xff0c;与 这个类是否被用到是无关的&#xff0c;每个类的load函数都会自动调用一次。 1 父类和子类都实现load函数的时候&#xff0c;父类的load方法优先于子类 2 类…

需求简报_代码简报:有史以来最怪诞的丑毛衣

需求简报Here are three stories we published this week that are worth your time:这是我们本周发布的三个值得您关注的故事&#xff1a; The geekiest ugly sweater ever: 4 minute read 有史以来最怪异的丑毛衣&#xff1a; 4分钟阅读 Lessons from my post-bootcamp job …

C#内置函数 RunSql的使用

作用批量执行sql语句表达式.RunSQL(SQLStatement,UseTransaction)表达式.一个代表DoCmd对象的变量。注释&#xff1a;sqlstatement参数的最大长度为 32,768 个字符&#xff08;而"宏"窗口中的 SQL 语句操作参数的最大长度为 256 个字符&#xff09;。 官方说仅能用于…

swif 在字符串中查找特定字符索引以及改变字符串的指定位置的颜色 字体大小

1 第一种方式 var text "谁包含这个字母";let range:Range<String.Index> text.range(of: "含")!;let end_idx:Int text.distance(from: text.startIndex, to: range.lowerBound);// 打印2print(end_idx);类方法 抽取 // 查找对应索引static func…

SD卡的控制方法(指令集和控制时序)

1.SD卡的命令格式&#xff1a; SD卡的指令由6字节(Byte)组成&#xff0c;如下&#xff1a; Byte1&#xff1a;0 1 x x x x x x(命令号&#xff0c;由指令标志定义&#xff0c;如CMD39为100111即16进制0x27&#xff0c;那么完整的CMD39第一字节为01100111&#xff0c;即0x270x40…

javascript 代码_代码简介:2016年JavaScript的现状

javascript 代码Here are three stories we published this week that are worth your time:这是我们本周发布的三个值得您关注的故事&#xff1a; The state of JavaScript in 2016: 5 minute read 2016年JavaScript状况&#xff1a; 阅读5分钟 Upgrading to macOS Sierra wi…

Unity协程截图,WWWForm、WWW配合上传

先说一下原理。。 截图有两种方法&#xff0c;第一种&#xff1a; Application.CaptureScreenshot(url); 这个API可以截全屏并保存到指定路径 这里我们不采用此方法 下面的代码采用第二种方法&#xff0c;自己建一个Texture2D 这种方法灵活&#xff0c;操作性更高 WWWForm方法是…

OC 的反射机制以及使用场景

OC 的反射机制 一 定义概念 普遍的概念就是类似于java的反射机制&#xff0c;动态机制使得OC语言更加灵活。 反射机制就是可以根据指定的类名获取类的相关信息。 二 作用 1 根据类名获得class // 选择器 和字符串之间的相互转化 FOUNDATION_EXPORT NSString *NSStringFr…

centos 网卡聚合及Cisco交换机链路聚合

一、配置环境 centos 系统。网卡1口和2口做链路聚合。 交换机网口 6口和7口。 二、服务器操作步骤 centos 6 1.创建一个channel bonding interface #vi /etc/sysconfig/network-scripts/ifcfg-bond0 添加如下几行&#xff1a; GATEWAY192.168.10.1 DNS1202.106.0.20 DEVICEb…

graphql redux_如何在Redux应用程序中使用GraphQL

graphql reduxby Howon Song通过宋颂恩 如何在Redux应用程序中使用GraphQL (How to use GraphQL in your Redux app) Fetching and managing data in Redux requires too much work. As Sashko Stubailo points out:在Redux中获取和管理数据需要太多的工作。 正如Sashko Stuba…

原创:去繁存简,回归本源:微信小程序公开课信息分析《一》

以前我开过一些帖子&#xff0c;我们内部也做过一些讨论&#xff0c;我们从张小龙的碎屏图中 &#xff0c;发现了重要讯息&#xff1a; 1&#xff1a;微信支付将成为重要场景&#xff1b; 2&#xff1a;这些应用与春节关系不小&#xff0c;很多应用在春节时&#xff0c;有重要的…

ubuntu 14.0 下github 配置

一&#xff1a;创建Repositories 1:首先在github下创建一个帐号。这个不用多说&#xff0c;然后创建一个Repositories。 2:然后在ubuntu下安装git相关的东东&#xff1a; 1sudo apt-get install git-core git-gui git-doc -y 3:在ubuntu本地创建一个ssh密匙&#xff1a; 1ssh-k…

OC 消息转发实现多继承

消息转发实现多继承 在OC 中&#xff0c;一个类只支持单继承&#xff0c;但是可以通过别的手段实现多继承。 利用消息转发实现多继承。 在OC 中&#xff0c;对象调用方法实际是在发消息&#xff0c;对象接收到一条消息的时候&#xff0c;消息函数随着对象的isa 指针到自己的…

chatscript_如何使用ChatScript构建您的第一个聊天机器人

chatscriptby Giorgio Robino通过乔治罗宾诺(Giorgio Robino) 如何使用ChatScript构建您的第一个聊天机器人 (How to build your first chatbot using ChatScript) 10–10–2018: article updated with new github repo url.2018年10月10日&#xff1a;文章更新为新的github r…