Non-content record storage

Topics: Troubleshooting, Writing modules
May 16, 2013 at 8:13 AM
Edited May 16, 2013 at 8:17 AM
Hello, I want to store non-content record in database. I've created model:
public class MyTypeDescriptor
    public virtual int Id { get; set; }

    public virtual Guid Guid { get; set; }

    public virtual string Name { get; set; }
and migrations:
                table =>
                table.Column("Id", DbType.Int32, command => command.PrimaryKey().Identity())
                     .Column("Guid", DbType.Guid, command => command.Unique().NotNull().WithDefault(Guid.Empty))
                     .Column("Name", DbType.String, command => command.Unique().NotNull().WithDefault("invalid_name")));
I've also created simple service:
public class MyTypeDescriptorAccess : IMyTypeDescriptorAccess

    private readonly IRepository<MyTypeDescriptor> repository; 

    public MyTypeDescriptorAccess (IRepository<MyTypeDescriptor> repository)
        this.repository = repository;

    public void Create(MyObjectTypeDescriptor myType)
        using (new TransactionScope(TransactionScopeOption.Suppress))
Unfortunalety, calling Create method causes exception:
TransactionScope nested incorrectly.
Calling Create without TransactionScope also casues errors. Using Required or RequiredNew options not helping.

What's wrong?
May 16, 2013 at 9:09 AM
Why do you want to use nested transactions?
May 16, 2013 at 9:21 AM
Edited May 16, 2013 at 9:24 AM
I do not need. But without transaction scope it is not working (exception with IsolationLevel).
I'll provide message in a moment.
May 16, 2013 at 9:33 AM
Edited May 16, 2013 at 9:34 AM
Exception when not using Suppress:
While preparing INSERT INTO MyModule_Module1_MyTypeDescriptor (Guid, Name) VALUES (@p0, @p1) an error occurred"}
Inner exception
{"The connection object can not be enlisted in transaction scope."}
May 16, 2013 at 9:39 AM
If it has a matter: Create method is called in TypePartEditorUpdate method in the driver.
May 16, 2013 at 10:28 AM
You shouldn't need to scope a new transaction for a simple repository create call (and generally you don't need to deal with transactions except cancelling it when only using built-in services). Have you tried without wrapping the call into anything?
May 16, 2013 at 10:31 AM
Edited May 16, 2013 at 10:32 AM
When and how is your code called?
What database provider are you using?

It shouldn't need an extra transaction within Orchard as it will have one anyway with each work scope. But if you are calling it outside of Orchard's scope it will need a transaction.

I ran into this kind of thing when implementing SignalR on versions 1.5 & 1.6 so there are conditions where this can arise.

IMyTypeDescriptorAccess - what does that implement? IDependency?
May 16, 2013 at 11:32 AM
Edited May 16, 2013 at 11:35 AM
That's the point, thanks! IMyTypeDescriptorAccess was implementing ISingletonDependency, when I changed it to IDependency it worked. Why it happens? Is sth special about ISingletonDependency and TrasactionScope? In that case instance of MyTypeDescriptorAccess was outside Orchard's scope or what?
May 22, 2013 at 8:50 AM
Edited May 22, 2013 at 8:52 AM
A few words of clarification:)

If you're going to access database (eg. via IRepository<T>) in your custom dependency, you need to either:
  • make sure the it is instantiated per request/work context (switching the class to be IDependency/IUnitOfWorkDependency instead of ISingletonDependency), or
  • inject and use Work<IRepository<T>> instead (or direct resolve through sth like IWorkContextAccessor.GetCurrent().Resolve<IRepository<T>>. It will ensure the instance you get will come from current "work" lifetime scope, not the "shell" one.
The above also applies to every other case when you need to mix singletons with per-request dependencies.

Also, please remember that IDependency interface does not ensure per-request instantiation, as opposed to IUnitOfWorkDependency. It's a common misconception. IDependency ensures instantiation in the same scope as the dependent component, so you will be able to inject it into singletons as well - please be aware of this.