От стороннего сервиса приходит WSDL, из которой студия генерит что-то вроде следующего:
public interface IMyInterface {
// CODEGEN: Generating message contract since the operation MyMethod is neither RPC nor document wrapped.
[System.ServiceModel.OperationContractAttribute(Action="http://failUrl/request", ReplyAction="http://failUrl/response")]
[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
Response MyMethod(Request request);
Можно ли поменять значения у атрибута?
Если в дизайн-тайме не получится, то хотя бы в рантайме
В самом крайнем случае — в бехавиоре, но пока непонятно в каком
Спасибо...
Re: Можно ли поменять атрибут у метода интерфейса?
Здравствуйте, mDmitriy, Вы писали:
D>Можно ли поменять значения у атрибута?
У меня вот что получилось:
using System;
using System.ComponentModel;
using System.Linq;
class Program
{
public class CustomAttribute : Attribute
{
public string Data { get; set; }
public CustomAttribute(string data) => Data = data;
}
[Custom("Hello, World!")]
public class Foo { }
static void Main()
{
var attr1 = TypeDescriptor.GetAttributes(typeof(Foo)).OfType<CustomAttribute>().FirstOrDefault();
Console.WriteLine("before: " + attr1.Data);
TypeDescriptor.AddAttributes(typeof(Foo), new CustomAttribute("Oops!"));
foreach (var attr2 in TypeDescriptor.GetAttributes(typeof(Foo)).OfType<CustomAttribute>())
{
Console.WriteLine("after: " + attr2.Data);
}
}
}
Результат:
before: Hello, World!
after: Oops!
Если я правильно понял, TypeDescriptor не умеет работать с несколькими экземплярами одного атрибута, и видимо поэтому при добавлении нового атрибута старый такой же автоматом убивается.
PS
Хотя эта штука, кажется, может работать только с атрибутами типов и свойств, не с методами.
Здравствуйте, alexzzzz, Вы писали: A>Результат: A>
before: Hello, World!
A>after: Oops!
Это совсем не то, к сожалению...
Мне надо поменять не атрибут у класса, а атрибут у метода интерфейса
И чтобы замена шла автоматом как минимум в рантайме без явного вызова заменяющего метода
Re: Можно ли поменять атрибут у метода интерфейса?
Здравствуйте, mDmitriy, Вы писали:
D>Всем привет!
D>Можно ли поменять значения у атрибута?
А что мешает скачать wsdl, в нём поменять. И только после этого генерить интерфейс?
Re: Можно ли поменять атрибут у метода интерфейса?
Здравствуйте, mDmitriy, Вы писали:
D>Можно ли поменять значения у атрибута? D>Если в дизайн-тайме не получится, то хотя бы в рантайме
А почему не получится в Design Time?
Там всего один метод, который вызывается для каждой сгенерированной операции.
Вам приходит OperationContractGenerationContext в котором вам понадобится свойство SyncMethod (если не рассматривать асинхронные операции). Это ничто иное как класс из CodeDOM (CodeTypeMember) у которого есть свойство CustomAttributes
Т.е. пишете расширение, которое будет получать все сгенерированные методы, смотреть их аттрибуты, находить "дефектные" и править.
Расширение нужно будет указать в конфиге проекта, для которого делаете генерацию Service References.
P.S. Простите, сейчас сходу пример не сделаю, просто уже бежать надо. Но если нужно будет, могу попробовать изобразить.
Re[2]: Можно ли поменять атрибут у метода интерфейса?
Здравствуйте, Михаил Романов, Вы писали: МР>P.S. Простите, сейчас сходу пример не сделаю, просто уже бежать надо. Но если нужно будет, могу попробовать изобразить.
Оказалось, что всё немного сложнее (или я за давностью лет что-то позабыл).
Вцелом, я всё написал правильно, но есть несколько нюансов, а именно IOperationContractGenerationExtension — не является самостоятельным расширением, как IWsdlImportExtension, т.е. первый без второго использовать нельзя.
Поэтому сама схема выглядит так:
1. Вы пишите IWsdlImportExtension, который к каждой операции, которую надо будет модифицировать при генерации, добавляет IOperationBehavior
2. Сам IOperationBehavior — можно вообще не реализовывать, но в том же классе нужно реализовать IOperationContractGenerationExtension.
Вот, собственно, пример, который у всех Action меняет хост (если он равен tempuri.org)
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Web.Services.Description;
using System.Xml;
using System.Xml.Schema;
namespace GenerationExtensions
{
public class OperationGenerationExtension : IWsdlImportExtension,
IOperationContractGenerationExtension,
IOperationBehavior
{
public void ImportContract(WsdlImporter importer, WsdlContractConversionContext context)
{
context.Contract.Operations.ToList().ForEach(opd => opd.OperationBehaviors.Add(this));
}
public void GenerateOperation(OperationContractGenerationContext context)
{
var operationContractAttributeType = new CodeTypeReference(typeof(OperationContractAttribute));
var method = context.SyncMethod;
var operationContractAttributes = method.CustomAttributes
.OfType<CodeAttributeDeclaration>()
.Where(attr => attr.AttributeType.BaseType == operationContractAttributeType.BaseType);
foreach (var attr in operationContractAttributes)
{
var actionArgument = attr.Arguments
.OfType<CodeAttributeArgument>()
.Single(arg => arg.Name == "Action");
var actionValue = (string)((CodePrimitiveExpression)actionArgument.Value).Value;
var uri = new UriBuilder(actionValue);
if (uri.Host == "tempuri.org")
{
uri.Host = "www.service.org";
var newUri = uri.ToString();
actionArgument.Value = new CodePrimitiveExpression(newUri);
};
}
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
}
public void BeforeImport(ServiceDescriptionCollection wsdlDocuments, XmlSchemaSet xmlSchemas, ICollection<XmlElement> policy)
{
}
public void ImportEndpoint(WsdlImporter importer, WsdlEndpointConversionContext context)
{
}
public void Validate(OperationDescription operationDescription)
{
}
}
}
Как видите, нужны буквально 2 метода:
— ImportContract (от IWsdlImportExtension) чтобы добавить behavior (самого себя).
— GenerateOperation (от IOperationContractGenerationExtension) — чтобы заменить парметр Action у атрибута [OperationContract(...)]
А подключать его как обычное WSDL Import расширение как я описывал здесь