Лично мне не нравятся неизвестные / нетипизированные свойства, поэтому я никогда не буду пытаться засунуть неизвестные типы в одно нетипизированное поле. Моим первым предпочтением было бы использовать одну плоскую бетонную структуру, аналогично тому, как вы уже проектировали бы таблицы СУБД с одной плоской таблицей из разных столбцов, собирающей всю информацию, которую вы хотите захватить, либо в самой таблице сущностей:
public class UserEntity
{
public long Id { get; set; }
//.. flattened properties of everything you want captured
}
Или, если мне нужно будет захватить одну и ту же информацию в нескольких таблицах, один класс со всеми свойствами, которые вы хотите захватить, например:
public class UserEntity
{
public long Id { get; set; }
//[Reference] // Optional: Save in external Animal table
public Animal MyPrimaryAnimal {get;set;}
}
public class Animal
{
public string Type { get; set; } // e.g. Cat, Dog, Bird
//.. flattened properties of everything you want captured
}
Свойства сложного типа автоматически поместите в OrmLite или добавьте атрибут [Reference]
, чтобы подключить Ссылки POCO OrmLite поддерживают , чтобы данные сохранялись во внешней таблице Animal
.
Вам потребуется добавить ссылку на FK в классе UserEntity
или Animal
для таких отображений 1: 1, как это
Различные свойства конкретного типа
Моим вторым предпочтением было бы иметь разные типизированные свойства для каждого отдельного свойства, которое я хочу сохранить, например:
public class UserEntity
{
public long Id { get; set; }
public CatEntity CatEntity { get; set; }
public DogEntity DogEntity { get; set; }
public BirdEntity BirdEntity { get; set; }
}
Тогда все будет работать как обычно, вы всегда будете иметь дело с бетоном типы при сохранении UserEntity
в OrmLite, который будет скрывать сложный тип за кулисами.
Сохранение неизвестных базовых типов в словаре объектов
Если мне абсолютно необходимо хранить разные объекты в одном поле, которое я бы сохранил в словаре объектов и предоставил типизированную оболочку для сохранения / извлечения базового типа сущности, например:
public class UserEntity
{
public long Id { get; set; }
[DataAnnotations.Ignore]
public AnimalEntity MyPrimaryAnimal
{
get => AnimalEntity.FromObjectDictionary(AnimalRef);
set => AnimalRef = value.ToObjectDictionary();
}
public Dictionary<string, object> AnimalRef { get; set; }
}
AnimalEntity
будет содержать все свойства базового типа и функцию фабрики для возврата конкретный тип на основе идентификатора Type
, например:
public class AnimalEntity
{
public string Type => GetType().Name;
public static AnimalEntity FromObjectDictionary(Dictionary<string, object> props)
{
if (props == null) return null;
var type = props[nameof(Type)];
switch (type)
{
case nameof(DogEntity):
return props.FromObjectDictionary<DogEntity>();
case nameof(CatEntity):
return props.FromObjectDictionary<CatEntity>();
case nameof(BirdEntity):
return props.FromObjectDictionary<BirdEntity>();
default:
throw new NotSupportedException($"Unknown Animal '{type}'");
}
}
}
Тогда вы можете иметь столько подтипов, сколько вы будете иметь sh:
public class CatEntity : AnimalEntity
{
public int Id { get; set; }
public string Cat { get; set; }
}
public class DogEntity : AnimalEntity
{
public int Id { get; set; }
public string Dog { get; set; }
}
public class BirdEntity : AnimalEntity
{
public int Id { get; set; }
public string Bird { get; set; }
}
, которые вы можете сохранить и повторно использовать как типизированные сущности, например:
db.Insert(new UserEntity {Id = 1, MyPrimaryAnimal = new BirdEntity {Id = 1, Bird = "B"}});
db.Insert(new UserEntity {Id = 2, MyPrimaryAnimal = new CatEntity {Id = 1, Cat = "C"}});
db.Insert(new UserEntity {Id = 3, MyPrimaryAnimal = new DogEntity {Id = 1, Dog = "D"}});
var results = db.Select<UserEntity>();
var animals = results.OrderBy(x => x.Id).Map(x => x.MyPrimaryAnimal);
animals[0] //= BirdEntity
animals[1] //= CatEntity
animals[2] //= DogEntity
Внешние ссылки
Если мне просто нужно одно поле для хранения ссылки на любую сущность в одном поле, обычно используется urn
public class UserEntity
{
public long Id { get; set; }
public string AnimalRef { get; set; }
}
Который вы можете использовать IdUtils.CreateUrn<T>
API ServiceStack или метод расширения ToUrn<T>
:
db.Insert(new UserEntity {Id = 1, AnimalRef = 1.ToUrn<BirdEntity>() });
db.Insert(new UserEntity {Id = 2, AnimalRef = 2.ToUrn<CatEntity>() });
db.Insert(new UserEntity {Id = 3, AnimalRef = 3.ToUrn<DogEntity>() });
Это сохранит следующие строковые ссылки:
urn:birdentity:1
urn:catentity:2
urn:dogentity:3
Если вы хотите загрузить ссылки, вам понадобится вспомогательная функция для разделения урны, сопоставления по типу и возврата ссылки на сущность по Id.