ORM своими руками
Как известно, программисты с давних пор пытаются упростить слой работы с базами данных и используют различные ORM. ORM имеют как достоинства так и недостатки, но цель статьи не описать их, а написать простенький ORM с описанием основных подходов в этом деле. Мир .NET пестрит разными реализациями, список доступных ORM для .NET можно посмотреть например здесь: .NET ORM List. Что будет уметь наш маппер? Работать с различными БД (первоначально тестирован на MS SQL и MS Access), использовать как простые так и составные первичные ключи, обеспечивать оптимистическую блокировку при многопользовательской работе, получать значения автоинкрементных полей и счетчиков автоматически. Ну и может являться платформой для собственных изысканий
Что НЕ реализовано: маппинг полиморфных моделей (ни в одном из 3-х подходов), оптимистические блокировки timestamp, маппинг связанных сущностей, поздняя загрузка и т д.
Итак, приступим. С одной стороны у нас есть классы .NET, с другой стороны таблицы базы данных. Каким образом можно описать связь между ними? Есть два основных подхода. Первый - использование аттрибутов (так делает например Linq to SQL), второй - использование внешнего описание маппинга, например xml (так делает например NHiberante).
Мне больше импонирует первый подход, поэтому попытаемся сделать что-то типа этого. В самом простом случае нам понадобится 2 аттирбута: для класса (маппинг на таблицу) и для его свойств (маппинг на поля).
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)]
public class DbTableAttribute : Attribute
{
private string _name;public DbTableAttribute(string name)
{
_name = name;
}public string Name
{
get { return _name; }
set { _name = value; }
}
}* This source code was highlighted with Source Code Highlighter.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class DbColumnAttribute : Attribute
{
private DbType _dbType;
private bool _canBeNull;
private bool _isPrimaryKey;
private bool _autoIncrement;
private bool _isVersion;
private string _name;public DbColumnAttribute(string name, DbType type, bool allowNull)
{
_canBeNull = true;
_isPrimaryKey = false;
_autoIncrement = false;
_name = name;
_dbType = type;
_canBeNull = allowNull;
}public DbType DbType
{
get { return _dbType; }
set { _dbType = value; }
}public bool CanBeNull
{
get { return _canBeNull; }
set { _canBeNull = value; }
}public bool IsPrimaryKey
{
get { return _isPrimaryKey; }
set { _isPrimaryKey = value; }
}public string Name
{
get { return _name; }
set { _name = value; }
}public bool AutoIncrement
{
get { return _autoIncrement; }
set { _autoIncrement = value; }
}public bool IsVersion
{
get { return _isVersion; }
set { _isVersion = value; }
}
}* This source code was highlighted with Source Code Highlighter.
Немножко описания. DbTableAttribute хранит одно поле - имя таблицы БД - хранилища данных этого типа. DbColumnAttribute хранит имя столбца таблицы, его тип данных, допускает ли ввод NULL значений, является ли автоинкрементом (назначается БД), и является ли столбцом версии (частный случай реализации оптимистической блокировки). Вот пример его использования:
[DbTable("Table1")]
public class Entity1
{
[DbColumn("Id", DbType.Int64, false, IsPrimaryKey = true, AutoIncrement = true)]
public int Id { get; set; }[DbColumn("name", DbType.String, true)]
public string Name { get; set; }[DbColumn("description", DbType.String, true)]
public string Description { get; set; }[DbColumn("createdTS", DbType.DateTime, false)]
public DateTime CreatedTs { get; set; }[DbColumn("val", DbType.Single, true)]
public float? Value { get; set; }[DbColumn("version", DbType.Int64, false,IsVersion = true)]
public long Version { get; set; }
}* This source code was highlighted with Source Code Highlighter.
а вот таблица БД на которую эта сущность маппится:
CREATE TABLE [dbo].[Table1](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[name] [nvarchar](50) NULL,
[description] [nvarchar](50) NULL,
[createdTS] [datetime] NOT NULL,
[val] [float] NULL,
[version] [bigint] NOT NULL CONSTRAINT [DF_Table1_version] DEFAULT ((0)),
CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]* This source code was highlighted with Source Code Highlighter.
Большой топик получается.. продолжу в следующем.
Ярик, у тебя опечатка “Link to SQL”
И зачем тебе вообще понадобился ORM своими руками?
Извините, был напуган, поправил
Зачем? даже не помню