Adding a Operation behavior at the ApplyDispatchBehavior of an endpoint behavior
- Hello.
Is it ok to add an operation behavior at the AppyDispatchBehavior method of an endpoint behavior?
Looking at the V1 code, I see that the operation behaviors are applied after the endpoint behaviors, so it should be ok to do that. I've already tried it and it behaves as expected.
However, at the documentation at http://msdn2.microsoft.com/en-us/library/ms730137.aspx states that the operation behaviors are applied before the endpoint behaviors.
Is this order an implementation detail that can change on a next version?
Thanks
Pedro Felix
Answers
The docs are wrong; I'll file a doc bug. The ordering is now very well-defined and will not change in the future.
That said, you can not add a behavior from another behavior, due to the fact that 'OnOpening' time is the last time you are allowed to modify the description (and adding a behavior is a modification to the description). This is (correctly) doc'd in the same document under the heading "Adding Behaviors Programmatically". Pragmatically, your observation is correct and adding an IOB from an IEB is likely to "work" today, but the docs forbid it and this product behavior may change in the future.
Is there a particular scenario you have in mind which caused you to go down this road?
Right. This is a well-known problem to us; in Orcas we are developing a terrific model for doing exactly this (better REST support and sometimes side-by-side REST and SOAP using the same contract), and we ran into exactly these issues.
Unfortunately for .Net Framework 3.0, the 'only way' to deal with the formatter issue is with code that runs before OnOpening(), which means you need a custom ServiceHost. This is unfortunate. Additionally, since operation formatter behaviors are part of the contract, this makes it practically impossible to use the same contract on two endpoints and have those endpoints have different formatters.
In Orcas, we are changing the way the formatter behaviors work to make these scenarios easier to implement. The non-legacy-breaking change we came up is this: the WCF v1 formatters change behavior so that, if there is already a formatter installed, they won't run. Existing apps still run the same, but this allows a user's IEndpointBehavior to insert a different formatter into each DispatchOperation/ClientOperation in the runtime, prior to the v1 operation formatter behaviors running. As a result, you can just configure an IEB that installs formatters, and this will pre-empt/turn-off the default-installed ones. As a result, you don't have to remove the default ones from the description, which means you don't have to change the description, which means it's a config-only solution, hurrah.
I don't know how clearly I explained that, but that's the story. For now you are kind of 'out of luck' and must use a custom host, but look forward to the next Orcas CTP and we will do our best to make you happy.

Yes you're wrong. If you do, e.g.
host.AddServiceEndpoint(typeof(IFoo), b1, a1);
host.AddServiceEndpoint(typeof(IFoo), b2, a2);then both endpoints have a reference to the same ContractDescription object (and thus the same OperationDescription objects). Does that make sense?
Pedro Felix wrote: However I’m thinking in the following strategy to achieve the same goal, assuming that the DispatchOperation and DispatchRuntime instances are different for each endpoint (are they?):
They are. (That is, the runtime objects are 'distinct per endpoint' whereas the description objects may be 'shared'.)
Your approach will work, with the exception that, assuming you have left the original formatters in the description, they will still run and thus do validation that may be inappropriate for your endpoint. That is, the operation-level behavior will be
-
v1 formatter-behavior runs, does validation (e.g. to ensure type is DataContract-serializable), installs formatter
-
your opBehavior runs, inspects context to decide if right endpoint to replace, replaces formatter
And so this 'works' provided that this part doesn't block your scenario.
Pedro Felix wrote: Ps. By the way, why do multiple endpoint use the same ContractDescription instance and not multiples clones?
There is no .Clone() on ContractDescription, and ContractDescriptions come from ServiceHostBase.CreateDescription() (which sets up a dictionary mapping from names to ContractDescriptions), so they way ServiceHostBase is designed, there is no good way to do that. That can either be construed as a design flaw (it may have been better for ServiceHostBase to have a GetFreshContractDescriptionFor(string name) or something) or a feature... there are lots of interesting interactions here that have different implications in different scenarios. I don't know all the history/rationale here, there are lots of interacting moving parts and you can certainly argue that we didn't land in the optimal spot in the design space (under some reasonable 'objective function' for judging optimal). So, yeah, I dunno.

-
- Yes, ChannelFactory<T> is the client-side dual of ServiceHost with regards to this kind of extensibility. (The custom ClientBase<T> is only necessary if you intend to svcutil-code-gen a whole class of clients that want to reuse the same base functionality, which perhaps you do.)
All Replies
The docs are wrong; I'll file a doc bug. The ordering is now very well-defined and will not change in the future.
That said, you can not add a behavior from another behavior, due to the fact that 'OnOpening' time is the last time you are allowed to modify the description (and adding a behavior is a modification to the description). This is (correctly) doc'd in the same document under the heading "Adding Behaviors Programmatically". Pragmatically, your observation is correct and adding an IOB from an IEB is likely to "work" today, but the docs forbid it and this product behavior may change in the future.
Is there a particular scenario you have in mind which caused you to go down this road?
Hello:
Thanks for your clear answer.
I "went down this road" when implementing a scenario similar to the one in the “Operation Formatter and Operation Selector” SDK example. However, this example has one characteristic that I was trying to avoid: the addition (in code) of the operation behaviors before the host is opened. I know that an alternative solution is to write a custom service host, but I was trying to avoid that too. Since I only want to change the behavior of the operations that belong to the endpoint with the custom behavior, I thought on this approach.My end goal is to create a binding and an endpoint behavior that will enable “REST-like” services in WCF, side by side with previous SOAP services. I’m trying to design a solution that only requires changes in the configuration file.
Thanks for your help
Pedro Felix
Right. This is a well-known problem to us; in Orcas we are developing a terrific model for doing exactly this (better REST support and sometimes side-by-side REST and SOAP using the same contract), and we ran into exactly these issues.
Unfortunately for .Net Framework 3.0, the 'only way' to deal with the formatter issue is with code that runs before OnOpening(), which means you need a custom ServiceHost. This is unfortunate. Additionally, since operation formatter behaviors are part of the contract, this makes it practically impossible to use the same contract on two endpoints and have those endpoints have different formatters.
In Orcas, we are changing the way the formatter behaviors work to make these scenarios easier to implement. The non-legacy-breaking change we came up is this: the WCF v1 formatters change behavior so that, if there is already a formatter installed, they won't run. Existing apps still run the same, but this allows a user's IEndpointBehavior to insert a different formatter into each DispatchOperation/ClientOperation in the runtime, prior to the v1 operation formatter behaviors running. As a result, you can just configure an IEB that installs formatters, and this will pre-empt/turn-off the default-installed ones. As a result, you don't have to remove the default ones from the description, which means you don't have to change the description, which means it's a config-only solution, hurrah.
I don't know how clearly I explained that, but that's the story. For now you are kind of 'out of luck' and must use a custom host, but look forward to the next Orcas CTP and we will do our best to make you happy.

- Hello
Thanks for your quick reply.
I've a question regarding your following statement: "Additionally, since operation formatter behaviors are part of the contract, this makes it practically impossible to use the same contract on two endpoints and have those endpoints have different formatters."
I thought that the operation description was scoped by the endpoint description, that is, I could have operation behaviors that apply for only one endpoint. Am I wrong?
Thanks
Pedro Felix Yes you're wrong. If you do, e.g.
host.AddServiceEndpoint(typeof(IFoo), b1, a1);
host.AddServiceEndpoint(typeof(IFoo), b2, a2);then both endpoints have a reference to the same ContractDescription object (and thus the same OperationDescription objects). Does that make sense?
Thanks a lot for your replies. They have been very instructive!
Your explanation is perfectly clear to me. I just thought that the ServiceDescription was a tree with no shared nodes (e.g no shared ContractDescription instances, even if they refer to the same contract). I didn’t read this anywhere (nor the opposite), it just made more sense to me.
Now I fully understand your statement: “Additionally, since operation formatter behaviors are part of the contract, this makes it practically impossible to use the same contract on two endpoints and have those endpoints have different formatters.
”However I’m thinking in the following strategy to achieve the same goal, assuming that the DispatchOperation and DispatchRuntime instances are different for each endpoint (are they?):
-
The ApplyDispatchBehavior method first checks to see if it is “under” the right endpoint. If not, the behavior is not applied.
-
The ApplyDispatchBehavior method performs this check by looking at the DispathRuntime (DispatchOperation.Parent) instance and searching for some characteristic that is particular to that endpoint (e.g the OperationSelector)
-
This uses the fact that the operation behaviors are applied after the endpoint behavior
Makes sense?
Once again, thanks for your replies.
Pedro Felix
Ps. By the way, why do multiple endpoint use the same ContractDescription instance and not multiples clones?
-
Pedro Felix wrote: However I’m thinking in the following strategy to achieve the same goal, assuming that the DispatchOperation and DispatchRuntime instances are different for each endpoint (are they?):
They are. (That is, the runtime objects are 'distinct per endpoint' whereas the description objects may be 'shared'.)
Your approach will work, with the exception that, assuming you have left the original formatters in the description, they will still run and thus do validation that may be inappropriate for your endpoint. That is, the operation-level behavior will be
-
v1 formatter-behavior runs, does validation (e.g. to ensure type is DataContract-serializable), installs formatter
-
your opBehavior runs, inspects context to decide if right endpoint to replace, replaces formatter
And so this 'works' provided that this part doesn't block your scenario.
Pedro Felix wrote: Ps. By the way, why do multiple endpoint use the same ContractDescription instance and not multiples clones?
There is no .Clone() on ContractDescription, and ContractDescriptions come from ServiceHostBase.CreateDescription() (which sets up a dictionary mapping from names to ContractDescriptions), so they way ServiceHostBase is designed, there is no good way to do that. That can either be construed as a design flaw (it may have been better for ServiceHostBase to have a GetFreshContractDescriptionFor(string name) or something) or a feature... there are lots of interesting interactions here that have different implications in different scenarios. I don't know all the history/rationale here, there are lots of interacting moving parts and you can certainly argue that we didn't land in the optimal spot in the design space (under some reasonable 'objective function' for judging optimal). So, yeah, I dunno.

-
- Thanks a lot.
Very instructive replies!
Pedro Felix - Hello again.
Just one more question regarding this subject.
If I want to make the same thing in the client side (replace the operation formatters) should I define a custom ChannelFactory<T> and a custom ClientBase<T> similar to the way that I define a custom ServiceHost in the service-side? These custom classes will change the service description at the last possible moment in order to replace the default operation behaviors.
Does this makes sense?
Pedro Felix - Yes, ChannelFactory<T> is the client-side dual of ServiceHost with regards to this kind of extensibility. (The custom ClientBase<T> is only necessary if you intend to svcutil-code-gen a whole class of clients that want to reuse the same base functionality, which perhaps you do.)
Pedro Felix wrote: However I’m thinking in the following strategy to achieve the same goal, assuming that the DispatchOperation and DispatchRuntime instances are different for each endpoint (are they?):
They are. (That is, the runtime objects are 'distinct per endpoint' whereas the description objects may be 'shared'.)
Your approach will work, with the exception that, assuming you have left the original formatters in the description, they will still run and thus do validation that may be inappropriate for your endpoint. That is, the operation-level behavior will be
-
v1 formatter-behavior runs, does validation (e.g. to ensure type is DataContract-serializable), installs formatter
-
your opBehavior runs, inspects context to decide if right endpoint to replace, replaces formatter
And so this 'works' provided that this part doesn't block your scenario.
Pedro Felix wrote: Ps. By the way, why do multiple endpoint use the same ContractDescription instance and not multiples clones?
There is no .Clone() on ContractDescription, and ContractDescriptions come from ServiceHostBase.CreateDescription() (which sets up a dictionary mapping from names to ContractDescriptions), so they way ServiceHostBase is designed, there is no good way to do that. That can either be construed as a design flaw (it may have been better for ServiceHostBase to have a GetFreshContractDescriptionFor(string name) or something) or a feature... there are lots of interesting interactions here that have different implications in different scenarios. I don't know all the history/rationale here, there are lots of interacting moving parts and you can certainly argue that we didn't land in the optimal spot in the design space (under some reasonable 'objective function' for judging optimal). So, yeah, I dunno.

Have there been any improvements on this issue in 3.5? I'm having the exact same problem in that I need a different IDataContractSurrogate for each endpoint (and none for other endpoints) on a service that has multiple contacts and each contract within the service has multiple endpoints. This is to handle pox, json, soap, and binary clients from a single service instance which will return different data structures in different scenarios.
If I can find a place to plug in and force the service to create an instance of ContractDescription per endpoint my issues will be resolved. Currently the only solution I can think of is to make one "big" IDataContractSurrogate that is somehow smart enough to figure out which endpoint is being used and to morph the data as needed.
-


