[ELMA3] Архитектура объектной модели
Общие принципы
Объектная модель системы – набор классов, сущностей, перечислений, которые используются в предметной области. Под сущностью в системе ELMA будем понимать объект, способный храниться в базе данных. Основные классы для работы с объектной моделью представлены в сборке EleWise.ELMA.SDK в пространствах имен, начинающихся с EleWise.ELMA.Model.
Работа с объектной моделью системы ELMA построена на следующих принципах:
Метаданные
Описание объектной модели в системе ELMA основано на использовании метаданных.
Метаданные – данные, описывающие некоторый элемент объектной модели (например, тип сущности, перечисление и т.д.).
Для создания метаданных (создания типов сущностей, перечислений) используется специальный плагин к Visual Studio, позволяющий визуально описывать модель системы (см. статью Редактор сущностей). Основные типы метаданных представлены в пространстве имен EleWise.ELMA.Model.Metadata. В файловой системе метаданные хранятся в файлах формата .md (в действительности - обычный xml-файл).
Пример метаданных сущности:
<?xml version="1.0" encoding="utf-8"?>
<Entity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IsUnique="true">
<Uid>04d5bcbf-4288-4d95-9745-1ebd0d690bb3</Uid>
<Name>User</Name>
<DisplayName>Пользователь</DisplayName>
<Namespace>EleWise.ELMA.Model.Entities.Security</Namespace>
<Properties>
<PropertyMetadata xsi:type="EntityPropertyMetadata">
<Uid>9357aacc-d37f-497a-99f4-4a106765f8e1</Uid>
<Name>UserName</Name>
<DisplayName>Учетная запись</DisplayName>
<TypeUid>9b9aac17-22bb-425c-aa93-9c02c5146965</TypeUid>
<Settings xsi:type="StringSettings">
<FieldName>UserName</FieldName>
</Settings>
<Required>true</Required>
<Filterable>true</Filterable>
</PropertyMetadata>
<PropertyMetadata xsi:type="EntityPropertyMetadata">
<Uid>a1e9ab10-6dad-4a11-a190-8f9d11b9ed77</Uid>
<Name>Password</Name>
<DisplayName>Пароль</DisplayName>
<TypeUid>9b9aac17-22bb-425c-aa93-9c02c5146965</TypeUid>
<Settings xsi:type="StringSettings">
<FieldName>Password</FieldName>
</Settings>
</PropertyMetadata>
</Properties>
<Type>Interface</Type>
<ImplementationUid>18faf3ae-03c9-4e64-b02a-95dd63e54c4d</ImplementationUid>
<TableName>User</TableName>
<Filterable>true</Filterable>
</Entity>
На основе метаданных автоматически генерируется исходный код на C# с типами, которые определяют метаданные. В данном случае будет автоматически сгенерирован следующий код:
namespace EleWise.ELMA.Model.Entities.Security
{
/// <summary>
/// Пользователь
/// </summary>
[MetadataType(typeof(EntityMetadata))]
[EntityMetadataType(EntityMetadataType.Interface)]
[Uid("04d5bcbf-4288-4d95-9745-1ebd0d690bb3")]
[ImplementationUid("18faf3ae-03c9-4e64-b02a-95dd63e54c4d")]
public partial interface IUser : IEntity<long>
{
/// <summary>
/// Учетная запись
/// </summary>
string UserName { get; set; }
/// <summary>
/// Пароль
/// </summary>
string Password { get; set; }
}
}
NHibernate
Для работы с базой данных в ELMA используется ORM-компонент NHibernate.Для упрощения работы с NHibernate в SDK ELMA реализован следующий функционал:
- маппинги сущностей создаются автоматически при создании через расширение для Visual Studio;
- работа с сессиями ведется в менеджерах сущностей;
- для менеджеров сущностей существует базовый класс, реализующий основные методы для работы с ними (Load, Save, Find и т.д.);
- транзакционность обеспечивается навешиванием атрибута TransactionAttribute на методы менеджера.
Менеджеры
Менеджер – некий сервис, предназначенный для работы с одним или несколькими элементами объектной модели. Обычно менеджеры создаются для работы с сущностями (см. статью Менеджеры сущностей).
Расширяемость объектной модели
Расширяемость объектной модели – принцип, позволяющий модулям системы создавать свои модели, а также расширять существующие (например, добавлять свойства в сущности из другого модуля. Данный принцип основан на использовании интерфейсов сущностей и интерфейсов расширения сущностей (см. статью Сущности).
Модули с объектными моделями
Элементы объектной модели (сущности, перечисления) можно создавать в любой сборке при наличии у нее атрибута EleWise.ELMA.Model.Attributes.ModelAssemblyAttribute.
Добавить его можно двумя способами:
1. Вручную – прописав в Properties\AssemblyInfo.cs следующую строчку:
[assembly: EleWise.ELMA.Model.Attributes.ModelAssemblyAttribute]
2. Используя расширение для Visual Studio, добавив элемент с типом ELMA Assembly Info и поставив галочку Сборка содержит модели.
Сущности
См. статью Сущности.
Работа с метаданными
Для работы с метаданными существует служба EleWise.ELMA.Model.Services.IMetadataRuntimeService.
namespace EleWise.ELMA.Model.Services
{
/// <summary>
/// Интерфейс сервиса для получения информации о метаданных
/// </summary>
[ExtensionPoint]
public interface IMetadataRuntimeService
{
/// <summary>
/// Получить тип по уникальному идентификатору
/// </summary>
/// <param name="uid">Уникальный идентификатор типа</param>
/// <param name="loadImplementation">Загружать ли метаданные реализации, если указанный тип - интерфейс</param>
/// <returns>Тип</returns>
Type GetTypeByUid(Guid uid, bool loadImplementation = true);
/// <summary>
/// Получить метаданные по уникальному идентификатору
/// </summary>
/// <param name="uid">Уникальный идентификатор метаданных</param>
/// <param name="loadImplementation">Загружать ли метаданные реализации, если указанный тип - интерфейс</param>
/// <returns>Метаданные</returns>
IMetadata GetMetadataByUid(Guid uid, bool loadImplementation = true);
/// <summary>
/// Зарегистрировать тип
/// </summary>
/// <param name="type">Тип</param>
void RegisterType(Type type);
/// <summary>
/// Получить список метаданных
/// </summary>
/// <returns>Список метаданных</returns>
IEnumerable<IMetadata> GetMetadataList();
/// <summary>
/// Получить описание типа данных по его уникальному идентификатору. Если не нашли - null
/// </summary>
/// <param name="typeUid">Уникальный идентификатор типа данных</param>
/// <returns>Описание типа данных</returns>
ITypeDescriptor GetTypeDescriptor(Guid typeUid);
/// <summary>
/// Получить описание типа данных по его типу CLR. Если не нашли - null
/// </summary>
/// <param name="runtimeType">Тип CLR</param>
/// <returns>Описание типа данных</returns>
ITypeDescriptor GetTypeDescriptor(Type runtimeType);
/// <summary>
/// Получить описание типа данных, который можно использовать для первичного ключа, по его уникальному идентфикатору. Если не нашли - null
/// </summary>
/// <param name="typeUid">Уникальный идентификатор типа данных</param>
/// <returns>Описание типа данных</returns>
ITypeDescriptor GetIdTypeDescriptor(Guid typeUid);
}
}