Я слышал, как некоторые люди говорили, что перечисления являются злом и не должны использоваться в веб-сервисах из-за несоответствий, которые могут возникнуть между сервером и клиентом, если присвоены некоторые значения или если перечисление помечено Атрибут Flags. Они также сказали, что веб-сервисы, предоставляющие перечисления, труднее поддерживать, но не смогли дать мне жизнеспособных аргументов. Итак, исходя из вашего опыта, каковы плюсы и минусы использования перечислений в веб-службе WCF?
Вы используете типы перечислений в своих веб-службах WCF?
Ответы (9)
Причина, по которой люди рекомендуют избегать перечислений в веб-сервисах, заключается в том, что они создают тонкие проблемы обратной совместимости.
То же самое относится и к обычным перечислениям, но в веб-сервисах проблема еще более очевидна, особенно в прокси-серверах, созданных .NET (см. Ниже).
- Если перечисление вводится только, у вас нет проблем.
- If the enumerate can be an out parameter then if you add a new element and you return it, old clients could have problems:
- If the client is using a .NET-generated proxy it will break before the caller can handle it (in the deserialization)
- Даже если сгенерированный код для прокси-сервера поддерживал изменение (например, если он сопоставляет перечисление со строкой), пользовательский код в клиенте может некорректно обрабатывать новое неожиданное значение (это может легко быть неисполненным путем)
Определяя параметр как строку, вы сигнализируете пользователю вашего API, что значение может измениться в будущем. Даже если вы думаете, что ценность никогда не изменится, лучше быть наготове.
На эту тему есть хороший пост Дэра Обасанджо.
Я использовал перечисления в WCF, а также в сценариях взаимодействия. Если вы контролируете обе стороны сервиса, с ним легче работать. Если вы контролируете только одну сторону службы, вам нужно обратить внимание на проблемы, о которых вы упомянули.
Перечисления намного лучше, чем строковые переменные или что еще вы можете использовать. Использование строк вместо перечислений - это антипаттерн, называемый в SOA «Свободный Гуси».
Перечисления полностью поддерживаются в WSDL и XSD через элемент схемы xsd:enumeration
. Он обеспечивает поддержку как отдельных значений, так и перечислений в стиле флагов, где несколько значений в перечислении флагов разделяются пробелами.
Таким образом, у вас не должно возникнуть проблем с использованием перечислений на любых платформах, совместимых со стандартами.
Конечно, все зависит от того, где вы собираетесь использовать эту службу WCF.
Если его будет использовать одно приложение, то изменение контракта не повлияет.
Если это несколько внутренних приложений, изменение контракта может потребовать некоторых изменений в других приложениях.
И, наконец, если служба WCF является общедоступной, вам, возможно, придется предоставить две версии службы с разными версиями, чтобы у людей, использующих их, было время перенести свою версию клиента в новую службу.
Честно говоря, все зависит от ваших потребностей.
Использование чего-либо еще, кроме перечислений, не решает проблему совместимости, а только скрывает ее. Предположим, вы используете int для замены перечисления. Вы действительно решили проблему совместимости или просто замаскировали ее, пока среда выполнения клиента не обнаружит неизвестное значение?
Однако стоит упомянуть одну вещь: прокси-серверы WCF не воссоздают явно заданные числовые значения перечисления. Если перечисление объявлено с "дырами", например
enum ErrorCodes
{
OK = 0,
GenericError = 100,
SomeOtherError = 101,
}
представление на стороне клиента будет таким
enum ErrorCodes
{
OK,
GenericError,
SomeOtherError,
}
... что на клиенте приводит к тому, что (int) ErrorCodes.GenericError равняется 1.
У вас будет синтаксическая эквивалентность, но не числовая эквивалентность.
Перечисления в WSDL следует рассматривать как необходимость обслуживания.
Добавление или удаление значения перечисления является (должно быть!) Триггером для серьезного обновления интерфейса. Если перечисление является выходным значением, вам обязательно нужно определить новую версию WSDL через новый URI, чтобы текущие клиенты не разорвали установленный контракт («что, если они получат одно из этих новых, неожиданных значений взамен? ? ") Если перечисление является входным значением, вы можете рассматривать это как незначительное обновление (" поскольку текущие клиенты не должны знать об этом новом значении "), но тогда это единственный способ для этих клиентов извлечь выгоду из добавление этой новой опции / функции (вы добавили это новое значение перечисления по какой-то причине, не так ли?) будет заключаться в том, чтобы попросить их переключиться, позже или раньше, на новую версию интерфейса.
Я думаю, это не имеет отношения к функциональному значению перечисления.
Придерживайтесь передового опыта, и вы будете в безопасности.
Я без проблем использовал перечисления в своих службах на основе WCF. Упомянутые вами возможные проблемы, безусловно, следует учитывать, хотя, если вы убедитесь, что применяете перечисления в довольно статичных ситуациях, у вас, вероятно, не будет особых проблем.
Вот подход. Может это громоздко. Мне просто очень не нравится, что я не могу использовать перечисления.
Он изящно обрабатывает десериализацию нераспознанного значения, вместо этого возвращая значение по умолчанию. Значение по умолчанию должно быть безопасным - либо приемлемый откат, либо что-то, что приложение может распознать как исключительное. (Как «Не указано».)
Расширения предотвращают необходимость проверки нуля перед сравнением.
[DataContract]
public class EnumValue<T> where T : struct
{
[DataMember]
private string _raw = string.Empty;
[IgnoreDataMember]
private bool _parsed;
[IgnoreDataMember]
private T _parsedValue;
public EnumValue()
{
Set(default(T));
}
public EnumValue(T value)
{
Set(value);
}
internal T Value
{
get
{
if (_parsed) return _parsedValue;
if (!Enum.TryParse<T>(_raw, out _parsedValue))
{
_parsedValue = default(T);
}
_parsed = true;
return _parsedValue;
}
}
public void Set(T value)
{
_raw = value.ToString();
_parsedValue = value;
_parsed = true;
}
}
public static class EnumValueExtensions
{
public static T GetValue<T>(this EnumValue<T> enumValue) where T : struct
{
return enumValue == null ? default(T) : enumValue.Value;
}
public static bool EqualsValue<T>(this EnumValue<T> enumValue, T compareTo) where T : struct
{
return (enumValue.GetValue().Equals(compareTo));
}
}
Сюжет из реального мира (значения изменены на анонимность). Используйте, чтобы иметь неявный enum
в приложении
public enum { orange, banana, mango }
Некоторый рефакторинг порядка и новых значений, и мы решили сделать это явным:
public enum { orange=1, banana=2, grape=3, mango=4 }
выглядит безобидно ...
Затем взрывается веб-сайт. Почесать голову, проверить службу, добавить отладочные сообщения, все в порядке.
Днем в кроличью нору, причина в базовой службе wcf, которая использовала перечисление в качестве возвращаемого типа.
Очевидно, Wcf не любит перечисления без значения по умолчанию.
Исправить:
public enum { wcfBugBane=0, orange=1, banana=2, grape=3, mango=4 }
Так ... это может тебя укусить.