К несчастью для меня, у меня был случай, когда класс, который должен быть сериализован, имел свойства с интерфейсами как свойствами, поэтому мне нужно было рекурсивно обрабатывать каждое свойство. Кроме того, некоторые свойства интерфейса были отмечены как [XmlIgnore], поэтому я хотел пропустить их. Я взял идеи, которые я нашел в этой теме, и добавил некоторые вещи к ней, чтобы сделать ее рекурсивной. Здесь показан только код десериализации:
void main () {var serializer = GetDataContractSerializer & lt; MyObjectWithCascadingInterfaces & gt; (); используя (FileStream stream = новый FileStream (xmlPath, FileMode.Open)) {XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader (поток, новый XmlDictionaryReaderQuotas ()); var obj = (MyObjectWithCascadingInterfaces) serializer.ReadObject (reader); // ваш код здесь}} DataContractSerializer GetDataContractSerializer & lt; T & gt; (), где T: new () {Тип [] types = GetTypesForInterfaces & lt; T & gt; (); // Отфильтровать дубликаты Тип [] result = types.ToList (). Distinct (). ToList (). ToArray (); var obj = new T (); вернуть новый DataContractSerializer (obj.GetType (), типы); } Тип [] GetTypesForInterfaces & lt; T & gt; (), где T: new () {return GetTypesForInterfaces (typeof (T)); } Тип [] GetTypesForInterfaces (Тип T) {Тип [] result = new Тип [0]; var obj = Activator.CreateInstance (T); // получить тип для всех свойств интерфейса, которые не помечены как «XmlIgnore» Type [] types = T.GetProperties () .Where (p = & gt; p.PropertyType.IsInterface & amp;! p.GetCustomAttributes (typeof ( System.Xml.Serialization.XmlIgnoreAttribute), false) .Any ()). Выбрать (p = & gt; p.GetValue (obj, null) .GetType ()) .ToArray (); result = result.ToList (). Concat (types.ToList ()). ToArray (); // делаем то же самое для каждого из типов, определенных foreach (тип t в типах) {Type [] embeddedTypes = GetTypesForInterfaces (t); result = result.ToList (). Concat (embeddedTypes.ToList ()). ToArray (); } return result; }
Квасной прав; триггер был бы лучшим способом для этого.
Вот код:
СОЗДАТЬ ИЛИ ЗАМЕНИТЬ ФУНКЦИЮ ensce_photo_count () RETURNS trigger AS $$ DECLARE max_photo_count INTEGER: = 10 ; photo_count INTEGER: = 0; must_check BOOLEAN: = false; BEGIN IF TG_OP = 'INSERT' THEN must_check: = true; END IF; IF TG_OP = 'UPDATE' THEN IF (NEW.owner! = OLD.owner) THEN must_check: = true; END IF; END IF; IF must_check THEN - предотвращать одновременные вставки из нескольких транзакций LOCK TABLE photos В ЭКСКЛЮЗИВНОМ РЕЖИМЕ; SELECT INTO photo_count COUNT (*) FROM photos WHERE owner = NEW.owner; IF photo_count & gt; = max_photo_count ТОГДА ИСКЛЮЧЕНИЕ ПОДЪЕМА «Невозможно вставить больше, чем% фотографий для каждого пользователя.», Max_photo_count; END IF; END IF; ВОЗВРАТ НОВЫЙ; КОНЕЦ; $$ LANGUAGE plpgsql; CREATE TRIGGER enforce_photo_count ПЕРЕД ВСТАВКОЙ ИЛИ ОБНОВЛЕНИЕМ НА ФОТОГРАФИЯХ ДЛЯ КАЖДОЙ ПРОЦЕДУРЫ ROW EXECUTE enforce_photo_count ();
Я включил блокировку таблицы, чтобы избежать ситуаций, когда два одновременных tansactions будут считать фотографии для пользователя, см., что текущий счетчик равен 1 ниже предела, а затем оба вставки, которые могли бы вызвать вы должны перейти 1 за пределы. Если это вас не волнует, было бы лучше удалить блокировку, поскольку она может стать узким местом со множеством вставок / обновлений.
Вы не можете записать такое ограничение в объявлении таблицы.
Существуют некоторые способы обхода:
photo_order
, который сохранит порядок фотографий, сделайте (user_id, photo_order)
UNIQUE
и добавьте CHECK (photo_order BETWEEN 1 И 10)
Другим подходом было бы добавить столбец «photo_count» в таблицу users, обновить его с помощью триггеров, чтобы он отражал реальность, и добавить его, чтобы обеспечить максимальное количество фотографий.
Боковое преимущество от это то, что в любой момент мы знаем (без учета), сколько у пользователя фотографий.
С другой стороны, подход Quassnoi также довольно крут, поскольку он дает вам возможность переупорядочивать фотографии в случае пользователь хотел бы этого.