Добрый день.
У меня следующая проблема.
Есть DAL класс, наследуемый от DataAccessor, в нем объявлен абстрактный метод:
[SprocName("Projects_Insert")]
public abstract override int Insert(Project project);
Из класса бизнес логики вызываю этот метод, все работает нормально. Но как только пытаюсь поместить его в пределы транзакции он перестает работать.
Делаю следующее — в DAL классе переопределяю метод GetDbManager():
private static DbManager db = new DbManager();
public override DbManager GetDbManager()
{
return db;
}
т.е. теперь он гарантированно возвращает один и тот же экземпляр DbManager.
В классе бизнес логики получаю ссылку на db и заворачиваю все в транзакцию:
DbManager db = DAL.GetDbManager();
db.BeginTransaction();
projectId = DAL.Insert(project);
db.CommitTransaction();
Так вот почему то этот код не работает, т.е. данные не вставляются. При этом в методе Insert используется тот же DbManager что и при открытии и коммите транзакции, т.к. если смотреть выполнение под дебагером, то перед вызовом Insert вызывается переопределенный GetDbManager(). Кроме того если б для Insert использовался другой DbManager, запись вставлялась бы по любому, т.к. вставка была бы не внутри транзакции. Такое чувство возникает, будто абстрактная реализация метода что-то делает с объектом DbManager и транзакция открывается нормально но вот не коммитится.
Если же метод в DAL классе сделать не абстрактным и то все работает.
В чем может быть проблема?
Спасибо.
P.S. Транзакция мне нужна для того чтобы потом добавить еще несколько обращений к БД, т.е. один метод внутри транзакции — это пока для теста.
Здравствуйте, bolikdimon, Вы писали:
B>Если же метод в DAL классе сделать не абстрактным и то все работает. B>В чем может быть проблема?
у тебя твой DbManager диспозится в конце вызова Insert. Ты попробуй ещё раз дернуть Insert и увидешь что он сфалит. А так как транзакция не была покоммичена, то в диспозе она откатывается.
DataAccessorEx — реализует 3ри способа создания DbManager'а, а так же временное кеширование созданного DbManager'а, как раз для реализации транзакции. Перед началом транзации вызывается CacheDbManager(), по окончанию ReleaseCachedDbManager(). Да BeginTransaction и CommitTransaction нужно вызывать явно. Хотя можно добавить к акцессору BeginTransaction() и [Commit/Rollback]Transaction() а внутри них кешировать созданный DbManager.
public class DbManagerCreationException : DataException
{
public DbManagerCreationException()
: base("An BLToolkit Database Manager creation failure.")
{
}
public DbManagerCreationException(String message)
: base(message)
{
}
public DbManagerCreationException(String message, Exception innerException)
: base(message, innerException)
{
}
public DbManagerCreationException(Exception innerException)
: base(innerException.Message, innerException)
{
}
protected DbManagerCreationException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
public delegate DbManager DbManagerCreator(Object argument);
public abstract class DataAccessorEx : BLToolkit.DataAccess.DataAccessor
{
protected override DbManager CreateDbManager()
{
try
{
DbManager db =
(null != _dbManagerCreator
? _dbManagerCreator(_dbManagerCreatorArgument)
: (!String.IsNullOrEmpty(_configurationString)
? new DbManager(_configurationString)
: base.CreateDbManager()
)
);
if (null != db)
{
if (_refCount > 0)
SetDbManager(db, false);
return db;
}
}
catch (DbManagerCreationException)
{
throw;
}
catch (Exception ex)
{
throw new DbManagerCreationException(ex);
}
throw new DbManagerCreationException();
}
public void CacheDbManager()
{
CacheDbManager(true);
}
public void CacheDbManager(Boolean immediateCreadeDbManager)
{
if (0 == _refCount)
{
DbManager db = (immediateCreadeDbManager ? CreateDbManager() : null);
_saveDbManager = DbManager;
_saveDisposeDbManager = DisposeDbManager;
SetDbManager(db, false);
}
_refCount++;
}
public void ReleaseCachedDbManager()
{
if (_refCount > 0 && 0 == --_refCount)
{
if (null != DbManager)
DbManager.Dispose();
SetDbManager(_saveDbManager, _saveDisposeDbManager);
_saveDbManager = null;
_saveDisposeDbManager = true;
}
}
public static T CreateInstance<T>(String configurationString)
where T : DataAccessorEx
{
T da = CreateInstance<T>();
da._configurationString = configurationString;
return da;
}
public static T CreateInstance<T>(DbManagerCreator dbManagerCreator, Object dbManagerCreatorArgument)
where T : DataAccessorEx
{
T da = CreateInstance<T>();
da._dbManagerCreator = dbManagerCreator;
da._dbManagerCreatorArgument = dbManagerCreatorArgument;
return da;
}
private String _configurationString;
private DbManagerCreator _dbManagerCreator;
private Object _dbManagerCreatorArgument;
private UInt32 _refCount = 0;
private DbManager _saveDbManager = null;
private Boolean _saveDisposeDbManager = true;
}
Спасибо за помощь. Помог следующий вариант:
В классе бизнес логики создал новый DbManager и этот же экзампляр использовал и в конструкторе аксессора DAL класса, т.е.
DbManager db = new DbManager();
DAL dal = DAL.CreateInstance(db);
db.BeginTransaction();
dal.Insert(...);
db.CommitTransaction();
Здравствуйте, bolikdimon, Вы писали:
B>Добрый день. B>У меня следующая проблема. B>Есть DAL класс, наследуемый от DataAccessor, в нем объявлен абстрактный метод:
B>[SprocName("Projects_Insert")] B>public abstract override int Insert(Project project);
B>Из класса бизнес логики вызываю этот метод, все работает нормально. Но как только пытаюсь поместить его в пределы транзакции он перестает работать.
Как вариант. Для использования в транзакциях я создаю методы с DbManager параметром:
public abstract int Insert(DbManager db, Project project);
Далее в другом методе DAL или BLL:
void DoSome(Project project)
{
MyAccessor a = GetAccessor();
using (DbManager db = a.GetDbManager())
{
db.BeginTransaction();
a.Insert(db, project);
db.CommitTransaction();
}
}
... << RSDN@Home 1.2.0 alpha rev. 771>>
Если нам не помогут, то мы тоже никого не пощадим.