Можно ли поменять атрибут у метода интерфейса?
От: mDmitriy Россия  
Дата: 27.10.17 11:54
Оценка:
Всем привет!

От стороннего сервиса приходит 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);

Это совершенно не рабочий вариант
Рабочий такой:
[System.ServiceModel.OperationContractAttribute(Action="http://www.service.com/SDFSDF", ReplyAction="*")]

Можно ли поменять значения у атрибута?
Если в дизайн-тайме не получится, то хотя бы в рантайме
В самом крайнем случае — в бехавиоре, но пока непонятно в каком
Спасибо...
Re: Можно ли поменять атрибут у метода интерфейса?
От: alexzzzz  
Дата: 27.10.17 12:39
Оценка:
Может, чужую библиотечку пропатчить при помощи Reflexil?

PS
Хотя, возможно, мой ответ ни к селу ни к городу, потому что слова WSDL и бехавиор мне незнакомы.
Отредактировано 27.10.2017 12:53 alexzzzz . Предыдущая версия .
Re: Можно ли поменять атрибут у метода интерфейса?
От: alexzzzz  
Дата: 27.10.17 14:11
Оценка:
Здравствуйте, 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
Хотя эта штука, кажется, может работать только с атрибутами типов и свойств, не с методами.
Отредактировано 27.10.2017 14:30 alexzzzz . Предыдущая версия .
Re[2]: Можно ли поменять атрибут у метода интерфейса?
От: mDmitriy Россия  
Дата: 27.10.17 14:32
Оценка:
Здравствуйте, alexzzzz, Вы писали:
A>Результат:
A>

before: Hello, World!
A>after: Oops!

Это совсем не то, к сожалению...
Мне надо поменять не атрибут у класса, а атрибут у метода интерфейса
И чтобы замена шла автоматом как минимум в рантайме без явного вызова заменяющего метода
Re: Можно ли поменять атрибут у метода интерфейса?
От: kov_serg Россия  
Дата: 27.10.17 14:54
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Всем привет!


D>Можно ли поменять значения у атрибута?

А что мешает скачать wsdl, в нём поменять. И только после этого генерить интерфейс?
Re: Можно ли поменять атрибут у метода интерфейса?
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 27.10.17 15:08
Оценка: 10 (3)
Здравствуйте, mDmitriy, Вы писали:

D>Можно ли поменять значения у атрибута?

D>Если в дизайн-тайме не получится, то хотя бы в рантайме
А почему не получится в Design Time?

Вы ведь используете стандартные Service Reference?
Тогда можно реализовать IOperationContractGenerationExtension

Там всего один метод, который вызывается для каждой сгенерированной операции.
Вам приходит OperationContractGenerationContext в котором вам понадобится свойство SyncMethod (если не рассматривать асинхронные операции). Это ничто иное как класс из CodeDOM (CodeTypeMember) у которого есть свойство CustomAttributes

Т.е. пишете расширение, которое будет получать все сгенерированные методы, смотреть их аттрибуты, находить "дефектные" и править.
Расширение нужно будет указать в конфиге проекта, для которого делаете генерацию Service References.

P.S. Простите, сейчас сходу пример не сделаю, просто уже бежать надо. Но если нужно будет, могу попробовать изобразить.
Re[2]: Можно ли поменять атрибут у метода интерфейса?
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 28.10.17 15:42
Оценка: 112 (4)
Здравствуйте, Михаил Романов, Вы писали:

МР>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 расширение как я описывал здесь
Автор: Михаил Романов
Дата: 28.09.17
Отредактировано 28.10.2017 15:43 Михаил Романов . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.