in src/System.ServiceModel.Primitives/src/System/ServiceModel/Description/TypeLoader.cs [860:1097]
private OperationDescription CreateOperationDescription(ContractDescription contractDescription, MethodInfo methodInfo, MessageDirection direction,
ContractReflectionInfo reflectionInfo, ContractDescription declaringContract)
{
OperationContractAttribute opAttr = ServiceReflector.GetOperationContractAttribute(methodInfo);
if (opAttr == null)
{
return null;
}
if (ServiceReflector.HasEndMethodShape(methodInfo))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
SRP.Format(SRP.EndMethodsCannotBeDecoratedWithOperationContractAttribute,
methodInfo.Name, reflectionInfo.iface)));
}
Type taskTResult;
bool isTask = ServiceReflector.IsTask(methodInfo, out taskTResult);
bool isAsync = isTask ? false : ServiceReflector.IsBegin(opAttr, methodInfo);
XmlName operationName = NamingHelper.GetOperationName(ServiceReflector.GetLogicalName(methodInfo, isAsync, isTask), opAttr.Name);
opAttr.EnsureInvariants(methodInfo, operationName.EncodedName);
Collection<OperationDescription> operations = contractDescription.Operations.FindAll(operationName.EncodedName);
for (int i = 0; i < operations.Count; i++)
{
OperationDescription existingOp = operations[i];
if (existingOp.Messages[0].Direction == direction)
{
// if we have already seen a task-based method with the same name, we need to throw.
if (existingOp.TaskMethod != null && isTask)
{
string method1Name = existingOp.OperationMethod.Name;
string method2Name = methodInfo.Name;
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.CannotHaveTwoOperationsWithTheSameName3, method1Name, method2Name, reflectionInfo.iface)));
}
if (isAsync && (existingOp.BeginMethod != null))
{
string method1Name = existingOp.BeginMethod.Name;
string method2Name = methodInfo.Name;
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.CannotHaveTwoOperationsWithTheSameName3, method1Name, method2Name, reflectionInfo.iface)));
}
if (!isAsync && !isTask && (existingOp.SyncMethod != null))
{
string method1Name = existingOp.SyncMethod.Name;
string method2Name = methodInfo.Name;
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.CannotHaveTwoOperationsWithTheSameName3, method1Name, method2Name, reflectionInfo.iface)));
}
contractDescription.Operations.Remove(existingOp);
OperationDescription newOp = CreateOperationDescription(contractDescription,
methodInfo,
direction,
reflectionInfo,
declaringContract);
newOp.HasNoDisposableParameters = ServiceReflector.HasNoDisposableParameters(methodInfo);
if (isTask)
{
existingOp.TaskMethod = newOp.TaskMethod;
existingOp.TaskTResult = newOp.TaskTResult;
if (existingOp.SyncMethod != null)
{
// Task vs. Sync
VerifyConsistency(new SyncTaskOperationConsistencyVerifier(existingOp, newOp));
}
else
{
// Task vs. Async
VerifyConsistency(new TaskAsyncOperationConsistencyVerifier(newOp, existingOp));
}
return existingOp;
}
else if (isAsync)
{
existingOp.BeginMethod = newOp.BeginMethod;
existingOp.EndMethod = newOp.EndMethod;
if (existingOp.SyncMethod != null)
{
// Async vs. Sync
VerifyConsistency(new SyncAsyncOperationConsistencyVerifier(existingOp, newOp));
}
else
{
// Async vs. Task
VerifyConsistency(new TaskAsyncOperationConsistencyVerifier(existingOp, newOp));
}
return existingOp;
}
else
{
newOp.BeginMethod = existingOp.BeginMethod;
newOp.EndMethod = existingOp.EndMethod;
newOp.TaskMethod = existingOp.TaskMethod;
newOp.TaskTResult = existingOp.TaskTResult;
if (existingOp.TaskMethod != null)
{
// Sync vs. Task
VerifyConsistency(new SyncTaskOperationConsistencyVerifier(newOp, existingOp));
}
else
{
// Sync vs. Async
VerifyConsistency(new SyncAsyncOperationConsistencyVerifier(newOp, existingOp));
}
return newOp;
}
}
}
OperationDescription operationDescription = new OperationDescription(operationName.EncodedName, declaringContract);
operationDescription.IsInitiating = opAttr.IsInitiating;
operationDescription.IsTerminating = opAttr.IsTerminating;
operationDescription.IsSessionOpenNotificationEnabled = opAttr.IsSessionOpenNotificationEnabled;
operationDescription.HasNoDisposableParameters = ServiceReflector.HasNoDisposableParameters(methodInfo);
if (opAttr.HasProtectionLevel)
{
throw ExceptionHelper.PlatformNotSupported("security: protectionLevel");
}
XmlQualifiedName contractQname = new XmlQualifiedName(declaringContract.Name, declaringContract.Namespace);
object[] methodAttributes = ServiceReflector.GetCustomAttributes(methodInfo, typeof(FaultContractAttribute), false);
if (opAttr.IsOneWay && methodAttributes.Length > 0)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.OneWayAndFaultsIncompatible2, methodInfo.DeclaringType.FullName, operationName.EncodedName)));
}
for (int i = 0; i < methodAttributes.Length; i++)
{
FaultContractAttribute knownFault = (FaultContractAttribute)methodAttributes[i];
FaultDescription faultDescription = CreateFaultDescription(knownFault, contractQname, declaringContract.Namespace, operationDescription.XmlName);
CheckDuplicateFaultContract(operationDescription.Faults, faultDescription, operationName.EncodedName);
operationDescription.Faults.Add(faultDescription);
}
methodAttributes = ServiceReflector.GetCustomAttributes(methodInfo, typeof(ServiceKnownTypeAttribute), false);
IEnumerable<Type> knownTypes = GetKnownTypes(methodAttributes, methodInfo);
foreach (Type knownType in knownTypes)
{
operationDescription.KnownTypes.Add(knownType);
}
MessageDirection requestDirection = direction;
MessageDirection responseDirection = MessageDirectionHelper.Opposite(direction);
string requestAction = NamingHelper.GetMessageAction(contractQname,
operationDescription.CodeName,
opAttr.Action,
false);
string responseAction = NamingHelper.GetMessageAction(contractQname,
operationDescription.CodeName,
opAttr.ReplyAction,
true);
XmlName wrapperName = operationName;
XmlName wrapperResponseName = GetBodyWrapperResponseName(operationName);
string wrapperNamespace = declaringContract.Namespace;
MessageDescription requestDescription = CreateMessageDescription(methodInfo,
isAsync,
isTask,
null,
null,
contractDescription.Namespace,
requestAction,
wrapperName,
wrapperNamespace,
requestDirection);
MessageDescription responseDescription = null;
operationDescription.Messages.Add(requestDescription);
MethodInfo outputMethod = methodInfo;
if (isTask)
{
operationDescription.TaskMethod = methodInfo;
operationDescription.TaskTResult = taskTResult;
}
else if (!isAsync)
{
operationDescription.SyncMethod = methodInfo;
}
else
{
outputMethod = ServiceReflector.GetEndMethod(methodInfo);
operationDescription.EndMethod = outputMethod;
operationDescription.BeginMethod = methodInfo;
}
if (!opAttr.IsOneWay)
{
XmlName returnValueName = GetReturnValueName(operationName);
responseDescription = CreateMessageDescription(outputMethod,
isAsync,
isTask,
taskTResult,
returnValueName,
contractDescription.Namespace,
responseAction,
wrapperResponseName,
wrapperNamespace,
responseDirection);
operationDescription.Messages.Add(responseDescription);
}
else
{
if ((!isTask && outputMethod.ReturnType != ServiceReflector.VoidType) || (isTask && taskTResult != ServiceReflector.VoidType) ||
ServiceReflector.HasOutputParameters(outputMethod, isAsync))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.ServiceOperationsMarkedWithIsOneWayTrueMust0));
}
if (opAttr.ReplyAction != null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.OneWayOperationShouldNotSpecifyAReplyAction1, operationName)));
}
}
if (!opAttr.IsOneWay)
{
if (responseDescription.IsVoid &&
(requestDescription.IsUntypedMessage || requestDescription.IsTypedMessage))
{
responseDescription.Body.WrapperName = responseDescription.Body.WrapperNamespace = null;
}
else if (requestDescription.IsVoid &&
(responseDescription.IsUntypedMessage || responseDescription.IsTypedMessage))
{
requestDescription.Body.WrapperName = requestDescription.Body.WrapperNamespace = null;
}
}
return operationDescription;
}