首先简单介绍以下PC遥控器是个神马东西。

你只需在被控电脑端安装上 PC遥控器,设置好微博登录账号(最新版同时支持新浪和腾讯微博)。这样无论你在何处何地,只要发个微博,就可以对你的电脑进行关机、重启、关闭显示器、查看屏幕截图等操作!简单方便!非常实用!

PC遥控器目前是靠我业余时间往里面添砖加瓦,但是一个人的力量和精力是有限的,为了让这款微博小应用的功能更加丰富和实用,我决定把PC遥控器开源,希望园里面有朋友能为PC遥控器贡献点代码。

PC遥控器的官方博客:http://suchuanyi.sinaapp.com/

源码是托管在github上面,地址是:https://github.com/terryso/PCRemote

PC遥控器有兴趣,想贡献代码的园友,你只需把你的源码提交到你自己的仓库,然后在github上面通知我去拉就可以了。

PC遥控器的源码里面还包含了一个我自己写的微博SDK,目前支持新浪和腾讯微博的API,只不过目前只包含PC遥控器用到的几个API而已。
不过在这个SDK的基础上新增一个API也是很简单的,对这个SDK有兴趣的园友也可以看看,给些意见。 

posted @ 2011-07-08 02:02 四眼蒙面侠 阅读(2732) 评论(36) 编辑

PC遥控器是一款新浪微博的PC端小工具,有了这个小工具,你就可以随时随地通过发微博来控制你家里的电脑。

使用场景:

有时你要急着出门,但是你想下载的电影还没下载完成,你希望回来的时候可以马上观看。这时PC遥控器就发挥作用了,你出门前只需打开PC遥控器,设置好微博账号后就可以了。两个小时后,你只需发一条微博:屏幕截图$$看看我的电脑在干什么。一分钟后,PC遥控器就会把你家里电脑当前的屏幕截图发到你的微博上。这时,你看到电影已经下载完成,马上发一条微博:关机$$省电,这时PC遥控就会帮你关闭你的电脑了,既省电又环保。

安装须知:

  1. WIN7直接安装就可以使用
  2. XP需要安装.net framework 3.5
  3. 下载地址:http://pcremote.sinaapp.com/publish.htm
  4. 产品主要:http://suchuanyi.sinaapp.com

命令格式:命令 + $$ + 任意内容 (任意内容就是指随便写什么都可以)

例子:关机$$今天天气真是哈,哈,哈

目前支持的命令列表:

命令作用
关机 默认5分钟后关闭计算机
注销 注销计算机
重启 重启计算机
关闭显示器 关闭显示器
减小音量 减小20%的音量
加大音量 加大20%的音量
静音 将计算机设置为静音
终止关机 取消关闭计算机
取消静音 取消静音
屏幕截图 把计算机当前的屏幕截图发到微博上
posted @ 2011-03-21 11:18 四眼蒙面侠 阅读(350) 评论(2) 编辑

In my first post, I explained a little bit about how sagas can be leveraged to deal with the problem of nested transactions—”transactions” that span more than a single message.  There were a few community questions related to me redefining the concept of a saga.  That’s definitely not what I’m trying to do.  Per my understanding, a saga sits there and handles multiple messages and exists to coordinate multiple distinct transactions and perform compensating actions relative to the entire “transaction” as a whole.  While by definition a saga may not be responsible for message ordering and de-duplication of messages, as a side-effect of implementation it can handle those things without difficulty.  If anything, the way I’m utilizing  sagas is perhaps somewhat too narrow—a selected subset of the overall meaning and capability of a saga.  Regardless, the main concern herein is how to apply event sourcing as the persistence mechanism for sagas.

One final thought before we look at some code: why should we even bother using event sourcing for sagas?  Why not just persist the state and be done with it?  That’s a great question.  In fact, there isn’t anything wrong with persisting the state and moving forward, per se.  But as we have seen when event sourcing is applied to aggregates, it gives us, as programmers, the ability to re-evaluate our model, adapt it, and even re-implement it in a completely different way.  Furthermore, in the community there have been various examples of the benefits of testing aggregates through watching the events that are persisted after handling a command.  Anyone who has done this can see that testing in this way is much more natural and it avoids the brittle tests that often exist when doing purely state-based testing against aggregates.  This same idea can be applied to sagas.  In essence, applying event sourcing to sagas allows our sagas to be more agile.

Okay…now the code.  How do we leverage event sourcing to implement a saga?  It’s actually not that bad.  Let’s examine at what ISaga might look like:

public interface ISaga
{
Guid Id { get; }
long Version { get; } // for optimistic concurrency

void Transition(object message);

ICollection GetUncommittedEvents();
void ClearUncommittedEvents();

ICollection GetUndispatchedMessages();
void ClearUndispatchedMessages();
}



This should look somewhat similar to the interface of an aggregate root that utilizes event sourcing.  The primary difference is that we now have an additional set of methods related to getting and clearing undispatched messages.



When a message arrives to be processed it is routed into the appropriate message handler whose “Handle” would look something like this:



public void Handle(OrderReceivedEvent message)
{
var sagaId = message.OrderId; // purchase correlation
var saga = repository.GetById(sagaId);
saga.Transition(message);
repository.Save(saga);
}



Granted, this could potentially be inlined into the saga itself, but for our purposes hear, I am choosing to keep the concept of handling a message separate from giving it to the saga.



We’re not attempting to use any kind of unit of work in this example.  We simply get a message, provide it to the saga, and save the saga using a repository.



The Transition method of such a saga is fairly straightforward:



void ISaga.Transition(OrderReceivedEvent message)
{
orderReceived = true;
orderTotal = message.Total;
stateMachine.Fire(Trigger.OrderReceived);

// to avoid duplication, these should be in a base class
version++;
uncommittedEvents.Add(message);
}



Typically the base class would be responsible for receiving event messages from the caller (in this case the bus message handler) and then routing it either dynamically or statically to the specific Transition method capable of working with the specific event message.



Be aware that we’re not using a 100% pure state machine here.  There are two member variables—“orderReceived” and “orderTotal”.  These variables give the saga a little bit of “memory”.  We will want those variables later.  “orderReceived” is used to help us determine if the state machine can transition to a completed state, while “orderTotal” will be used on the resulting command message once we transition to a Completed state.



(Implementation note: Rather than having separate boolean variables to track each message that is received, you can use an enum and concatenate it using bitwise operations, e.g. orderProgress = orderProgress | Progress.OrderReceived).



State Machines, Transitions, and Memory



In multi-message scenarios, especially as the number of messages being handled grows, it becomes increasingly difficult to use a pure state machine to know where things are without having some kind of “memory”.  This is like the “vending machine state machine” problem.  In that problem, every single pathway for every possible permutation of coin must be explicitly modeled up to the price of the item.  It gets nasty fast, especially as the different kinds of coins such as nickels and pennies are accepted and the price of the item increases.



By utilizing a little bit of “memory”, we are able to side-step a large amount of complexity to keep things simple.  This is where the “orderReceived” variable comes into play in the sample code above.  By using it along with other memory variables, we can leverage the best parts of a state machine:



stateMachine.Configure(OrderState.Open)
.PermitIf(Trigger.OrderReceived, OrderState.Completed,
() => orderReceived && paymentReceived);



The above code is an example of a state machine called Stateless, which is still actively developed as compared to SimpleStateMachine which that author has basically abandoned.  The above code says that when we are in the Open state and the OrderReceived trigger occurs, transition to the “Completed” state when the order has been received AND payment has been received.



When we transition to the Completed state is when something observable happens:



stateMachine.Configure(OrderState.Completed) 
.OnEntry(OnCompleted);

....

private void OnCompleted()
{
undispatchedMessages.Add(new DoSomethingInterestingCommand
{
OrderId = orderId,
Total = orderTotal,
...
});
}



In the above code, we have configured the state machine to invoke the OnCompleted event when the state transitions to Completed.  When that occurs, we add a new message to dispatch into our list of undispatched messages.  Later, during the repository “Save” operation, we’ll perform the actual dispatch.



Message Idempotency



In this above example, no matter how many times the OrderReceived message is handled, it will only result in the state transition to Completed once.  For messages that increment/decrement values, such as CashDeposited and the like, we could easily keep track of the identifier for the message and only increment the value if the message hasn’t been handled before.



Persistence



To load the saga from persist storage, we grab the committed events and transition a new instance of the particular type of saga:



public TSaga GetById<TSaga>(Guid sagaId) where TSaga : class, ISaga, new()
{
var saga = new TSaga(); // can be done in different ways

var stream = this.eventStore.Read(sagaId);
foreach (var @event in stream.Events)
saga.Transition(@event);

saga.ClearUncommittedEvents();
saga.ClearUndispatchedMessages();
return saga;
}



To persist a saga, we do the following:



public void Save(ISaga saga)
{
var events = saga.GetUncommittedEvents();
eventStore.Write(new UncommittedEventStream
{
Id = saga.Id,
Type = saga.GetType(),
Events = events,
ExpectedVersion = saga.Version - events.Count
});

foreach (var message in saga.GetUndispatchedMessages())
bus.Send(message); // can be done in different ways

saga.ClearUncommittedEvents();
saga.ClearUndispatchedMessages();
}



In case of an optimistic concurrency exception, our messaging infrastructure will re-deliver the message and we would rebuild the saga, but this time with the committed events from the other, competing process that caused the concurrency exception.  Then, we would re-apply the message that was the victim of the concurrency exception.



What About “Now”?



One quirk related to event sourcing is the concept of “now”.  Because you’re rebuilding the saga from scratch every time, you can’t persist what “now” is.  If you use DateTime.UtcNow just anywhere inside of your saga, it can result in subtle bugs because DateTime.UtcNow changes each time you rebuild the saga from the events.  A rule of thumb is to ensure that each incoming event message always carries with it the concept of time, e.g. when the order was accepted.  Any timeout messages that occur can also carry the time at which they timed out.  Lastly, any messages that are going to be dispatched can get DateTime.UtcNow.



Conclusion



Why use event sourcing for sagas?  Options.  Just like event sourcing for aggregates opens up a world of possibilities, it can do the same thing with sagas.  Even if you don’t buy into the idea of using event sourcing with sagas, do try to understand the importance of sagas as a pattern and where they fit in a messaging-oriented world.



I have applied the above concepts, including ISaga, SagaBase, and SagaRepository, into myCommonDomain project on GitHub.  Perhaps this isn’t quite the best place for the code, but I wanted to get something out for people to see.

posted @ 2011-01-27 00:07 四眼蒙面侠 阅读(77) 评论(0)  编辑

For starters, what is a saga?  A saga is a “long-lived business transaction or process”.  Okay, so what does that mean?  Well, first of all, the “long-lived” part doesn’t have to mean hours, days, or even weeks—it could literally mean something as short as a few seconds.  The amount of time is not the important part.  It’s the fact that the “transaction”, business process, or activity spans more than one message.  Udi has written several times on sagas.  His articles are always worth reading.  The foundational theory behind sagas is to avoid the use of blocking (and locking) transactions across lots of resources.  Locks are one of the primary enemies of scalability.

Late last week I was communicating with Rinat Abdullin on the CQRS Google Group about sagas and explaining how they might be implemented using event sourcing.  I have spoken of this to others in the past as well. In the aforementioned thread, I didn’t go into much detail about the “how”.  Hence this post.

Perhaps we should explain why a saga is even necessary when we have aggregates and domain objects that manage state.  Aggregates are great business objects that wrap a lot of business complexity.  The problem with aggregates is that they only care about their little part of the universe. Sagas, on the other hand, are coordinating objects.  They listen for what’s happening and they tell other objects to take appropriate action.  Picture them as choreographers—let’s try and avoid the words orchestra and orchestration altogether.  In essence, sagas listen for events and instruct other parts of the system to perform tasks based upon the events.  This is juxtaposed to aggregates which are told to do something and then alert the world that they performed some action.  This could be generalized into the following: Sagas listen to events and dispatch commands while aggregates receive commands and publish events.

Sagas manage process.  They contain business behavior, but only in the form of process.  This is a critical point.  Sagas, in their purest form, don’t contain business logic.  During Greg Young’s workshop, he hammered this one pretty hard.  Most programmers think “logic” and aren’t used to thinking “process”.  Let me put it this way.  If you have a saga with “if else” statements, you’ve got logic.  Process is best implemented using a state machine.  The state machine we use to manage process within each saga is called Stateless by Nicholas Blumhardt,the creator of Autofac.

In a message-oriented world, there are two fundamental problems—ordering and duplicates.  These are related to message infrastructure and its corresponding guarantees.  Sagas are able to act as a kind of firewall to the outside.  They encapsulate the mess and help shield the domain from much of the nasty details of reality.

With message ordering, let’s consider how a typical shipping or fulfillment department might utilize sagas to help carry out their responsibilities.  Let’s imagine that fulfillment is not to ship the goods until payment has been received.  As messages can arrive in any order, what would happen if “PaymentRecieved” was the first message to arrive?  In that situation, fulfillment wouldn’t even have a clue what to ship because they haven’t even been made aware of a corresponding order.  With sagas we can easily outline the various state transitions and then dispatch commands only when the appropriate conditions are met—such as the receipt of both the OrderReceived and PaymentReceived events, regardless of the sequence in which they arrive.

Looking at duplicates and idempotency with a state machine-based saga, it’s not hard to see that regardless of the number of times a message is received, it will only cause a state transition once.  Think about it like a DVD player. You hit stop and then stop again.  Not matter how many times you press stop, the state transition only occurs once.  So it is with sagas.  A message can be received multiple times but will only cause the desired state transition once.  This makes duplicate messages naturally idempotent.  Sweet.

The other really great thing about sagas is that they understand the concept of time.  For example, let’s suppose we wanted to notify first-time customers of a discount if they didn’t complete checkout within 48 hours.  The saga could implement a timeout by scheduling a timeout message to be sent back to the saga after 48 hours.  When the timeout message was received 48 hours later, the saga would attempt to perform the state transition related to the timeout.  If the customer did something in the last 48 hours, the timeout message wouldn’t cause a state transition.  But if the person had not yet completed checkout, the saga could dispatch a message to the appropriate component to take corresponding action.  This component would decide the “who” and the “how”.  All the saga did was to tell the component: “There hasn’t been any activity, notify the customer.”  The component would then decide whether or not to notify based upon business logic, e.g. is this a first-time customer, etc.

To Be Continued…

Now that we’ve explained why sagas are important and how state machines can help us implement a saga which deals with the complexity of duplicate messages, out-of-order messages, and timeout messages, in Part II of this article, we’ll look at some of the details of how to use event sourcing to create testable, robust, yet fluid sagas.

posted @ 2011-01-27 00:05 四眼蒙面侠 阅读(54) 评论(0)  编辑

  Brad Wilson在他自己的博客写了有关ASP.NET MVC3依赖注入支持的一系列文章,但由于某些原因(你懂的),需要翻墙才能阅读。

为了方便自己也方便喜欢的同学,特意翻墙再拷贝回来发布,以下是这一系列文章的索引:

  1. Part 1: Introduction
  2. Part 2: Controllers
  3. Part 3: View Engines/View Pages
  4. Part 4: Filters
  5. Part 5: IDependencyResolver
  6. Part 6: Model Validation 
  7. Part 7: Model Metadata
  8. Part 8: Value Providers
  9. Part 9: Model Binders
  10. Part 10: Controller Activator
  11. Part 11: View Page Activator  
posted @ 2011-01-25 18:22 四眼蒙面侠 阅读(155) 评论(3) 编辑

View Page Activator

In ASP.NET MVC, it’s common for views to be compiled into classes. In MVC 1.0, we shipped the WebFormViewEngine as the default view engine, which allowed the user to write views using the familiar <% %> syntax from ASP.NET WebForms. The first time the view is rendered, the view file goes through a code generation step, which is compiled into a class. The WebFormViewclass is responsible for creating and executing the class that resulted from the view.

In ASP.NET MVC 3, we introduced a new Razor view engine. Like the WebForm view engine, the .cshtml and .vbhtml files from Razor go through a code generation step at run-time which compiles down into classes. The RazorView class is the Razor view engine counterpart to the WebFormView class.

We have introduced a new base class (BuildManagerCompiledView) and a new service (IViewPageActivator) which is responsible for instantiating instances of the view classes that were compiled from the view files. We have made the IViewPageActivator instance findable via the dependency resolver.

Disclaimer

This blog post talks about ASP.NET MVC 3 Beta, which is a pre-release version. Specific technical details may change before the final release of MVC 3. This release is designed to elicit feedback on features with enough time to make meaningful changes before MVC 3 ships, so please comment on this blog post or contact me if you have comments.

New Base Class: BuildManagerCompiledView

When we introduced the new Razor view engine, it was clear that there was a lot of common code between the infrastructure classes of our two view engines; namely, the WebFormView and RazorView (which both implement the IView interface) had significant shared code that was centered around BuildManager.

In the ASP.NET runtime, BuildManager is the class that is responsible for converting view files (like .aspx and .ascx files in WebForms, and .cshtml and .vbhtml files in Razor) into the underlying classes using code generation. BuildManager uses build providers to do most of that transformation work (which is outside the scope of this blog post). The important takeaway is that we extracted a common base class for the two view classes: BuildManagerCompiledView. The logic which creates view page class instances was centralized into this class, and the new IViewPageActivator service was introduced to allow pluggability of the creation of the view page classes.

Developers which are creating view engines which use build providers and BuildManager to convert view files into classes can take advantage of this new base class for their implementation(s) of the IView interface.

Implementing IViewPageActivator

The new IViewPageActivator interface contains a single method for creating a view page instance:

public interface IViewPageActivator {
    object Create(ControllerContext controllerContext, Type type);
}

Given the controller context and the view page class type, implementers of this interface must create the view page instance.

Location: IViewPageActivator

This is a “singly registered” style service introduced in MVC 3. There is no static registration point for this service as its purpose is strictly to support dependency injection; as such, the only way to register an instance of this service is through the dependency resolver.

The logic in BuildManagerCompiledView consults the dependency resolver, calling GetSerivce(typeof(IViewPageActivator)) and using the provided service when present. If there is no IViewPageActivator present in the dependency resolver, we will then ask the dependency resolver to create the concrete view page type by calling GetService(viewPageType). If the dependency resolver also fails to create the concrete view page type, we finally fall back to the MVC 2 behavior of using Activator.CreateInstance to create the view page type.

What's Next?

This is the end of the road for MVC 3 Beta. We believe we are now reasonably feature complete for MVC 3 and dependency resolution. If there are specific areas where you wish the framework would better enable dependency resolution, please don't hesitate to discuss them on the MVC Forums.

Thanks for reading!

posted @ 2011-01-25 17:59 四眼蒙面侠 阅读(128) 评论(0) 编辑

Controller Activator

In MVC 1.0, we introduced IControllerFactory to allow better dependency injection of controller instances. We also provided theDefaultControllerFactory, which created controller instances with Activator.CreateInstance. Some of this is discussed in Part 2 of this series.

We realized that DefaultControllerFactory was actually doing two things: turning the controller name into the controller type, and then instantiating the instance of that type. In ASP.NET MVC 3, we’ve split out the action of instantiating the controller instance into a new service: IControllerActivator. We have made the IControllerActivator instance findable via the dependency resolver.

Disclaimer

This blog post talks about ASP.NET MVC 3 Beta, which is a pre-release version. Specific technical details may change before the final release of MVC 3. This release is designed to elicit feedback on features with enough time to make meaningful changes before MVC 3 ships, so please comment on this blog post or contact me if you have comments.

Implementing IControllerActivator

The new IControllerActivator signature is identical to the DefaultControllerFactory.GetControllerInstance virtual method:

public interface IControllerActivator {
    IController Create(RequestContext requestContext, Type controllerType);
}

Developers who previously implemented IControllerFactory by deriving from DefaultControllerFactory just to override the GetControllerInstance method for dependency injection purposes should now implement IControllerActivator instead.

Location: IControllerActivator

This is a “singly registered” style service introduced in MVC 3. There is no static registration point for this service as its purpose is strictly to support dependency injection; as such, the only way to register an instance of this service is through the dependency resolver.

The logic in DefaultControllerFactory was updated to consult the dependency resolver, calling GetSerivce(typeof(IControllerActivator)) and using the provided service when present. If there is no IControllerActivator present in the dependency resolver, we will then ask the dependency resolver to create the concrete controller type by calling GetService(controllerType). If the dependency resolver also fails to create the concrete controller type, we finally fall back to the MVC 2 behavior of using Activator.CreateInstance to create the controller type.

What’s Next?

In the final post of this series, we will discuss the new View Page Activator service.

posted @ 2011-01-25 17:58 四眼蒙面侠 阅读(122) 评论(0) 编辑

Value Providers

ASP.NET MVC 2 introduced a new method to find value providers: the ValueProviderFactory class. Value providers are used by the model binding system in MVC to populate the values of model objects. MVC includes value providers for several common value sources (including query string, form, route data, uploaded files, and JSON postbacks); MVC Futures includes several mode (including cookies, server values, session values, and temp data values). In ASP.NET MVC 3, we have made ValueProviderFactory instances findable via the dependency resolver.

Disclaimer

This blog post talks about ASP.NET MVC 3 Beta, which is a pre-release version. Specific technical details may change before the final release of MVC 3. This release is designed to elicit feedback on features with enough time to make meaningful changes before MVC 3 ships, so please comment on this blog post or contact me if you have comments.

Implementing ValueProviderFactory

Developers who implement this class must provide an implementation of GetValueProvider which, given a controller context, optionally returns an instance of a class which implements the IValueProvider interface. The developer may also choose to return null if there is no appropriate value provider or values.

For reference purposes, there are several implementations of ValueProviderFactory in the MVC and MVC Futures source code. For a simple example, see the pair of classes: QueryStringValueProviderFactory and QueryStringValueProvider.

Implementing IValueProvider

Developers who implement this interface provide implementations of two methods: ContainsPrefix and GetValue. The former method is used to determine if there are any values in the provider with the given prefix (so the model binding system knows when to stop recursively binding). The latter method is used to get the value for a specific key.

There are two implementations of IValueProvider (NameValueCollectionValueProvider and DictionaryValueProvider) will accept collections of values to be used for the value provider implementation. Most of the value providers in the MVC source code actually derive from one of these two base classes.

Location: ValueProviderFactory

This is a “multiply registered” style service introduced in MVC 2. The static registration point for this service is atValueProviderFactories.Factories for non-DI users.

The logic in ValueProviderFactoryCollection (which implements ValueProviderFactories.Factories) was updated to consult the dependency resolver, calling GetServices(typeof(ValueProviderFactory)) and adding any services found to the list of statically registered services. Value providers all collaborate to provide values for model binding, with a “first one to provide the value wins” strategy, so registration order is important. The factories found in the dependency resolver will always run in the order they are returned from the resolver (and before the statically registered value provider factories); similarly, the value providers will be consulted in the order they are returned from the factories.

What’s Next?

Next up we’ll talk about Model Binders.

posted @ 2011-01-25 17:57 四眼蒙面侠 阅读(78) 评论(0) 编辑

Model Binders

ASP.NET MVC 1.0 introduced the IModelBinder interface. Developers who implement this interface are responsible for creating models from values obtained from value providers. In ASP.NET MVC 3, we introduced a new interface (IModelBinderProvider) which allows developers to dynamically provide implementations of IModelBinder. We have made IModelBinderProvider instances findable via the dependency resolver.

Disclaimer

This blog post talks about ASP.NET MVC 3 Beta, which is a pre-release version. Specific technical details may change before the final release of MVC 3. This release is designed to elicit feedback on features with enough time to make meaningful changes before MVC 3 ships, so please comment on this blog post or contact me if you have comments.

Implementing IModelBinderProvider

In prior versions of MVC, developers could only register static mappings from types to model binder instances (throughModelBinders.Binders). While this satisfied simple uses, it made complex and powerful model binders a little more challenging to write.

When we decided to open model binding up for dependency resolution, we knew that we were going to need to introduce a layer of indirection, since model binders are usually only valid for a limited number of types (unless you are the default model binder).

We solved both problems by introducing a new interface:

public interface IModelBinderProvider {
    IModelBinder GetBinder(Type modelType);
}

Developers who implement this interface can optionally return an implementation of IModelBinder for a given type (they should return null if they cannot create a binder for the given type).

The system continues to rely on a “default model binder” if the system cannot find any appropriate model binder for the given type. You can change the default model binder by setting ModelBinders.Binders.DefaultBinder. Out of the box, this is set to an instance of DefaultModelBinder.

There are no implementations of IModelBinderProvider in MVC. However, there is a similar system in MVC Futures from MVC 2 (in the ModelBinding folder), and there are several model binder providers that could be used to spark ideas about writing providers for MVC 3, including model binder providers for arrays, binary data, collections, complex objects, and dictionaries.

Location: IModelBinderProvider

This is a “multiply registered” style service introduced in MVC 3. The static registration point for this service is at ModelBinderProviders.BinderProviders for non-DI users.

The logic in ModelBinderProviderCollection (which implements ModelBinderProviders.BinderProviders) consults the dependency resolver, calling GetSerivces(typeof(IModelBinderProvider)) and adding any services found to the list of statically registered services. Model binder provides are run in-order, with a “first one to provide a model binder wins” strategy, so registration order is important. The providers found in the dependency resolver will always run in the order they are returned from the resolver (and before the static registered providers).

What’s Next?

Next up we’ll look at the new Controller Activator service.

posted @ 2011-01-25 17:57 四眼蒙面侠 阅读(125) 评论(0) 编辑

Model Metadata

ASP.NET MVC 2 introduced an extensible model metadata system where developers could implement a class which derived from ModelMetadataProvider to provide meta-information about the models in the system. In ASP.NET MVC 3, we have made the metadata provider findable via the dependency resolver.

Disclaimer

This blog post talks about ASP.NET MVC 3 Beta, which is a pre-release version. Specific technical details may change before the final release of MVC 3. This release is designed to elicit feedback on features with enough time to make meaningful changes before MVC 3 ships, so please comment on this blog post or contact me if you have comments.

Writing a Metadata Provider

For more information about ModelMetadata and what would be involved in writing a metadata provider, please see my blog post ASP.NET MVC 2 Templates, Part 2: ModelMetadata.

Location: ModelMetadataProvider

This is a “singly registered” style service introduced in MVC 2. The static registration point for this service is atModelMetdataProviders.Current for non-DI users.

The model metadata provider inspects models, and returns meta-information about the models, include information like display names, formatting strings for display and editing, flags on whether a value is editable or not, etc.

The logic in ModelMetadataProviders was updated to attempt to find the implementation of ModelMetadataProvider first by calling DependencyResolver.GetService(typeof(ModelMetadataProvider)). If the service does not exist in the dependency resolver, then it falls back to the static registration point. If you register a model metadata provider in both the static registration point and in the dependency resolver, MVC will throw an exception (since the service is intended to be singly registered).

The default model metadata provider continues to be DataAnnotationsModelMetadataProvider.

What’s Next?

Next up in the series is Value Providers.

posted @ 2011-01-25 17:56 四眼蒙面侠 阅读(102) 评论(0) 编辑