Шей, спасибо за информацию. Основываясь на ответе «это невозможно», я сказал себе: держи свое пиво.
Итак, я что-то сделал. Что-то некрасивое, но я сделал это. И это работает.
Возьмем, в нашем случае, саму сущность, содержащую столбец ID и jsonb Data:
public class Entity<T> where T : EntityData
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
[Column(TypeName = "jsonb")]
public T Data { get; set; }
public Entity() : base()
{
}
}
Примечание: обобщенный c T - это EntityData, который ничто но пустой базовый класс для будущих целей. В действительности это может быть что угодно.
Затем мы можем использовать класс Entity<T>
следующим образом:
Entity<User> userEntity = new Entity<User>();
userEntity.Data.Username = "myusername"; // the .Data property is now of type User (the actual POCO/Model/JSON document)
Класс User имеет свойства:
public class User : EntityData
{
[Index] // custom IndexAttribute class
public string Username { get; set; }
}
Теперь Я создал функцию EnsureIndexes (), которую можно разместить внутри собственного класса DbContext / UnitOfWork или где угодно. Эта функция использует отражение и перебирает свойства сущности внутри Entity<T>.Data
и вызывает необработанный NpgsqlCommand:
// get DbSet<> properties of the context
PropertyInfo[] dbsets = context.GetType().GetProperties().Where(x => x.PropertyType.Name == "DbSet`1").ToArray();
foreach (PropertyInfo dbset in dbsets)
{
// get the generic type of the DbSet<> property.
// in this case we will get the Entity<T> type
string lowerCaseDbSetName = dbset.Name.ToLower();
Type dbsetDataType = dbset.PropertyType.GetGenericArguments()[0];
// try to obtain the Entity<T>.Data PropertyInfo
PropertyInfo dataProperty = dbsetDataType.GetProperty("Data");
if (dataProperty == null)
{
continue;
}
Type dataPropertyType = dataProperty.PropertyType;
// Now iterate over the properties of the POCO/Document stored in Entity<T>.Data
PropertyInfo[] jsonProperties = dataPropertyType.GetProperties().ToArray();
foreach (PropertyInfo jsonProperty in jsonProperties)
{
// If we can find an IndexAttribute....
IndexAttribute attribute = Attribute.GetCustomAttribute(jsonProperty, typeof(IndexAttribute)) as IndexAttribute;
if (attribute == null)
{
continue;
}
string camelCaseProperty = jsonProperty.Name.ToCamelCase();
string lowerCaseProperty = jsonProperty.Name.ToLower();
// We will then create an SQL statement to create an index on that property. Mainaining the standard Npgsql naming format of IX_DbSet_Property
string sql = $"CREATE INDEX IX_{dbset.Name}_{lowerCaseProperty} ON public.\"{dbset.Name}\" ((public.\"{dbset.Name}\".\"Data\" ->> '{camelCaseProperty}'));";
int result = context.ExecuteNonQuery(sql); // my custom function returns -2 on an exception
if (result != -2)
{
Console.WriteLine(sql);
}
}
}