Флаги с веб-сервисами
У меня есть перечисление атрибута флага, которое находится за веб-службой следующим образом:
[Serializable,Flags]
public enum AccessLevels
{
None = 0,
Read = 1,
Write = 2,
Full = Read | Write
}
Моя проблема заключается в том, что потребитель моего веб-сервиса не имеет исходных постоянных значений перечисления. В результате клиентская сторона класса прокси-сервера имеет следующее:
{
None = 1,
Read = 2,
Write = 4,
Full = 8
}
И, таким образом, когда потребитель проверяет доступ "Чтение", это будет ложным, даже если "testItem" является "Полный"
((testItem & Svc.Read) == Svc.Read)
Как я могу правильно предоставлять флаги через веб-службу?
EDIT:
Согласно этой статье, возможно, не удастся сделать то, что я ищу. Иван Кривяков утверждает, что
Несовершенная прозрачность перечислений
Оказалось, что перечисления не такие, как как мы хотели бы, чтобы они были. Есть три липких вопроса:
- Если код на стороне сервера объявляет перечисление и назначает определенный числовой значения для его членов, эти значения не будет виден клиенту.
- Если код на стороне сервера объявляет перечисление [Flags] с "составной" маской значения (как в White = Red | Green | Blue), это неправильно отражено на клиентская сторона.
- Если сервер или клиент передает "незаконное" значение, которое находится за пределами объем перечисления, он вызывает исключение в XML-де-сериализаторе на другая сторона.
Так что я задаюсь вопросом, является ли это просто ограничением и не возможно.
Ответы
Ответ 1
Я провел обширные исследования по этому вопросу и обнаружил, что невозможно сериализовать константы перечисления через веб-службу. Обратите внимание, что для достижения вашей цели вам не нужны перечисления None или Full. Эти два перечисления могут подразумеваться с помощью комбинации чтения/записи:
Вы можете использовать полный доступ, если ваш AccessLevels = Read | Написать и нет, если ваши AccessLevels = 0 [ничего]
Ваши перечисления будут выглядеть так:
[Serializable,Flags]
public enum AccessLevels
{
Read = 1,
Write = 2
}
Ответ 2
У меня была аналогичная проблема и обошел ее, добавив еще одну веб-службу, чтобы сначала вернуть значения флагов.
Затем они стали значениями, которые я использовал в сравнении.
Возможно, это не чистое решение, но оно работает.
Edit:
В моем первоначальном ответе было указано, что значения передаются как отдельная веб-служба, а не внутри перечисления.
Однако, выкалывая, кажется, что перечисление 0,1,2, сопоставленное с 1,2,4 через веб-службу (даже если установлено значение атрибута [Flags]), является общей проблемой.
Решение, предлагаемое рядом людей, состоит в том, чтобы изменить исходное определение перечисления и начать с 1, а не 0.
Ответ 3
Флаги должны быть кратными двум, а в вашем случае ваши флаги (0,1,2,3).
Попробуйте изменить свое определение структуры следующим образом:
[Serializable,Flags]
public enum AccessLevels{
None = 1,
Read = 2,
Write = 4,
Full = Read | Write}
И посмотрите, работает ли он лучше.
(Надеюсь, я не сделаю себе дурака, его поздно и Im на пути к кровати..)
Ответ 4
Один из вариантов - предоставить более подробный класс вместо enum, например.
[Serializable]
public class AccessPermission{
public boolean None{get;set;}
public boolean Read{get;set;}
public boolean Write{get;set;}
public boolean Full{get;set;}
public AccessPermission(AccessLevels level){
None = false;
Read = false;
Write = false;
Full = false;
switch(level){
case AccessLevels.None:
break;
case AccessLevels.Read:
Read = true;
break;
case AccessLevels.Write:
Write = true;
break;
case AccessLevels.Full:
Read = true;
Write = true;
Full = true;
break;
}
}
}
Другим вариантом, который я могу видеть, является предоставление метода в том, какой язык используется для успешного взаимодействия целого, которое вы отправляете. Оператор флага означает, что С# выполняет маскировку бит, чтобы определить, отмечен ли отдельный флаг
0001 -> None
0010 -> Read
0100 -> Write
0110 -> Full
поэтому для проверки любого разрешения вы должны увидеть, установлен ли этот бит
public static boolean CanRead(int accessLevel){
return (accessLevel | 2) > 0 // return true if read bit set, using bitwise or
}
public static boolean CanWrite(int accessLevel){
return (accessLevel | 4) > 0 // return true of write bit set.
}
Обратите внимание, что это второе решение более хрупкое, если вы измените определение accessLevels, которое ваш клиент будет молча пропустить.