Статьи

Обработка значений перечисления с помощью преобразователей типов в Windows Azure Mobile Services

Я много работал с Windows Azure Mobile Services (WAMS). Это блестящая технология, которая позволяет за считанные минуты поддерживать мощные службы, совместимые с OData, для поддержки приложений Магазина Windows 8, приложений Windows Phone 8 и даже приложений iOS. Трудно переоценить абсолютную удивительность этого материала.

В настоящее время я работаю над набором кода, который вскоре станет образцом проекта, в котором будут освещены приложения WAMS и Windows 8 (скоро появится проект под названием «FamilyPig»). В процессе этого я столкнулся с парой вопросов, один из которых я расскажу здесь, и дам несколько советов людям, которые могут столкнуться с подобным вопросом.

Одна из замечательных функций, с которой очень легко работать с WAMS, — это концепция «динамической схемы». В двух словах, это означает, что если у вас есть существующая таблица, и вы добавляете в нее простой старый объект CLR (POCO), используя метод InsertAsync ( IMobileServiceTable)интерфейс), WAMS достаточно умен, чтобы посмотреть на входящий объект и убедиться, что у него есть все столбцы, которые ему нужны в базовой таблице базы данных SQL Azure Windows для хранения записи (при условии, что на динамической схеме включена мобильный сервис). Если столбец существует, он создается на лету. Очень, очень круто. Обратите внимание, что на самом деле происходит под покровом, что ваш объект POCO преобразуется в объект JSON для передачи по проводам, а WAMS разбирает этот объект JSON, чтобы посмотреть на столбцы.

Теперь, чтобы сделать это, он делает некоторые предположения. Он смотрит на объект JSON и, если видит данные определенного типа, создает столбец определенного типа для поддержки данных. Самое простое, что можно было бы сделать, это просто создать текстовые столбцы в базовой базе данных SQL, поскольку любой тип данных может храниться в виде строки. Вместо этого он делает что-то более умное и создает типы данных, которые являются более «подходящими». Вот стратегия, которую он использует:

Тип данных JSON / CLR Тип T-SQL в базовой таблице базы данных SQL Azure
Числовые значения (целые, десятичные, с плавающей запятой) float (53) — это максимальная точность float .
логический немного
DateTime DateTimeOffset (3)
строка NVARCHAR (макс)

ОК, это здорово. Динамическая схема довольно крутая. Однако…

Когда я работаю со схемой базы данных, я часто сталкиваюсь с ситуацией, когда хочу создать таблицу стилей «код / ​​декодирование». В моем мире таблица «код / ​​декодирование» обычно представляет собой таблицу с двумя столбцами, где первичный ключ — это «код», а другой столбец — текстовое описание. Каноническим примером является таблица «States» (в США). Как это:

Код раскодировать
Алабама Алабама
Аляска Аляска
Аризона Аризона
Иллинойс Иллинойс
Вайоминг Вайоминг

Затем, с этой таблицей, я буду использовать ее везде, где мне нужно состояние… вот так:

колонка Тип данных Заметки
Имя NVARCHAR (50)  
Фамилия NVARCHAR (50)  
Адрес 1 NVARCHAR (100)  
Адрес 2 NVARCHAR (100)  
город NVARCHAR (100)  
государство символ (2) Внешний ключ к государственному коду
Почтовый индекс символ (5)  

Теперь, в моем текущем проекте, мне нужен был не код состояния, а « CurrencyType ». Более того, я хотел, чтобы в моем приложении можно было использовать значение «enum» для CurrencyTypes. Это заставило меня задуматься о том, как WAMS может обрабатывать хранение и извлечение «enum» в базе данных. Не сразу понятно, что он будет знать, что делать с ним как тип JSON. JavaScriptSerializer, кажется, превращает значения перечисления в целочисленное значение , что, вероятно, сработает, но я действительно хотел, чтобы в моей базовой таблице SQL хранилось «IL» для Иллинойса, а не число 14 в виде 53-битной точности с плавающей точкой. Итак, как я могу превратить мои значения перечисления в базовые коды состояния в виде 2-символьной строки при входе и преобразовать их обратно в мои значения перечисления при выходе?

Для начала вот (усеченная) версия моего enum для моего CurrencyType.

public enum CurrencyType
{
	UnitedArabEmiratesDirham,
	AfghanistanAfghani,
	AlbaniaLek,
	//...
	UnitedStatesDollar,	
	//... 
	Unknown
}

Затем секретный соус оказывается IDataMemberJsonConverter . Этот интерфейс существует в сборке Microsoft.WindowsAzure.MobileServices и имеет 2 метода, которые необходимо реализовать — ConvertFromJson и ConvertToJson . Вот преобразователь типов, который я создал, чтобы массировать мое перечисление в входящую строку, и обратно из строки в перечисление на обратном пути…

Во-первых, метод ConvertToJson:

public IJsonValue ConvertToJson(object instance)
{
	if (instance is Types.CurrencyType)
	{
		switch ((Types.CurrencyType)instance)
		{	
			case Types.CurrencyType.UnitedArabEmiratesDirham:
				return JsonValue.CreateStringValue("AED");
			case Types.CurrencyType.AfghanistanAfghani:
				return JsonValue.CreateStringValue("AFN");
			case Types.CurrencyType.AlbaniaLek:
				return JsonValue.CreateStringValue("ALL");
// SNIP
			case Types.CurrencyType.UnitedStatesDollar:
				return JsonValue.CreateStringValue("USD");
// SNIP
			default:
				return JsonValue.CreateStringValue("XXX");
		}
	}
	else
		return JsonValue.Parse("null");
}

Далее метод ConvertFromJson:

public object ConvertFromJson(IJsonValue value)
{ 
	CurrencyType result = Types.CurrencyType.Unknown; 
	if (value != null && value.ValueType == JsonValueType.String) 
	{ 
		switch (value.GetString()) 
		{ 
			case "AED": 
				return Types.CurrencyType.UnitedArabEmiratesDirham; 
			case "AFN": 
				return Types.CurrencyType.AfghanistanAfghani; 
			case "ALL": 
				return Types.CurrencyType.AlbaniaLek;
// SNIP 
			case "USD": 
				return Types.CurrencyType.UnitedStatesDollar;
// SNIP 
			case "ZWD": 
				return Types.CurrencyType.ZimbabweDollar; 
			case "XXX": 
				return Types.CurrencyType.Unknown; 
			default: 
				return Types.CurrencyType.Unknown; 
		} 
	}
	return result;
}

Теперь осталось только украсить класс CLR, который мы используем для хранения нашего объекта с достаточной информацией, чтобы сериализатор мог использовать наш конвертер типов по мере необходимости. В моем случае столбец для CurrencyType содержится в классе FamilyGroup.  Вот эта реализация:

public class FamilyGroup
{
	public long Id { get; set; } 
	public string Name { get; set; }

 	[DataMemberJsonConverter(ConverterType = typeof(CurrencyTypeConverter))] 
	public CurrencyType CurrencyType { get; set; }
}

Вот и все. У нас есть наш enum для работы с нашим кодом, мы можем сохранять и извлекать наши значения в WAMS, и данные в таблицах доступны для чтения для загрузки.

Удачного кодирования.