Hi,

I am trying to use a List<t> in my application. This list contains serveral items of the entity type "Component". I created an entity called Configuratie which has a EntitySet of type "Component". The user can created multiple "Components" before I save the "Configuration" item. To do this I at the Components to the List<t> and when I save the "Configuratie" item I loop through the List<t>.

But everytime I try to do this I get an error message

A first chance exception of type 'System.InvalidOperationException' occurred in Xtensive.Storage.dll System.InvalidOperationException: Unable to process operation without a transaction. Use Transaction.Open(...) to open it. at Xtensive.Storage.Session.OpenTransaction(IsolationLevel isolationLevel, Boolean autoTransaction) at Xtensive.Storage.Session.OpenTransaction(Boolean autoTransaction) at Xtensive.Storage.Transaction.Open(Session session, Boolean autoTransaction) at Xtensive.Storage.Aspects.TransactionalAspect.OnEntry(Object instance) at Xtensive.Storage.Persistent.GetFieldValueT at Xtensive.Storage.Internals.AssociationActionProvider.<.cctor>b__0(AssociationInfo association, IEntity owner) at Xtensive.Storage.PairIntegrity.SyncManager.Enlist(OperationType type, Entity owner, Entity target, AssociationInfo association) at Xtensive.Storage.EntitySetBase.Add(Entity item) at Xtensive.Storage.EntitySet1.Add(TItem item) at fiod.drs.client.NieuwConfiguratie.<>c__DisplayClass8.<Save>b__6(Component com) in D:\Projecten\Registratie\drs.client\Editors\NieuwConfiguratie.cs:line 123 at System.Collections.Generic.List1.ForEach(Action`1 action) at fiod.drs.client.NieuwConfiguratie.Save(String ibncode, String merk, String soort, String serienr, String opmerking, String type) in D:\Projecten\Registratie\drs.client\Editors\NieuwConfiguratie.cs:line 123The thread 0xf44 has exited with code 0 (0x0).

This is part of the code I use to add a "Component" item to a "Configuratie" items

using (var transactionScope = Transaction.Open())
            {
                var configuratie = new Configuratie()
                {
                    Object = Query<drs.model.Object>.SingleOrDefault(objectKey),
                    Gegevens = new ExtraInfo()
                    {
                        Ibncode = ibncode,
                        Merk = merk,
                        Soort = soort,
                        Serienummer = serienr,
                        Opmerking = opmerking,
                    },
                    Soort = (ConfiguratieTypes)configType
                };
                this.Schijven.ForEach(com => configuratie.Schijven.Add(com));

                transactionScope.Complete();
            }

Hopefully someone can help me with this problem.

Kind regards Martijn


Updated at 26.08.2009 11:37:08

Tnx for the reply. I added a SessionConfigration to my code, see below

var config = new SessionConfiguration() { Options = SessionOptions.AutoTransactions};

Change my

Session.Open(Program.Domain)

to

Session.Open(Program.Domain, config)

But still getting the same error...

So what I am doing wrong??

Btw..any samples available that implements a this kind of logic into a method og SessionBound? > Another way to achieve the same is to place all this logic into a method of SessionBound (Entity in particular). If auto transactions are enabled, its execution will be wrapped into a transaction.


Updated at 27.08.2009 10:27:19

Hi,

I checked which version of the Xtensive.Storage.dll (C:\Program Files\DataObjects.Net v4.0.5\Bin\Release\Xtensive.Storage.dll) I use below are de details about the dll

Version: 1.0.0.12358 Create date: 15-8-2009 6:21

This is the same for the Xtensive.Storage.dll located in the GAC.

Are there any newer files then this?

Kind regards, Martijn


Updated at 28.08.2009 9:56:02

Hello,

I did some testing on my own and it see that there error comes from the fact that I use different sessions for creating the "component" and "configuration" entities

See my example code below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Xtensive.Storage;
using Xtensive.Storage.Configuration;

using TestListType.Model;

namespace TestListType
{
    class Program
    {
        static List<Component> ComponentCollection = new List<Component>();
        static Domain domain;

        static void Main(string[] args)
        {
            var config = DomainConfiguration.Load("mssql");
            domain = Domain.Build(config);

            Console.WriteLine("Begin test run....");

            Console.WriteLine("Create component....");
            AddComponent();

            Console.WriteLine("Create configuration and add component....");
            AddComponentToConfiguration();

            Console.WriteLine("Done!!");
            Console.ReadLine();
        }

        static void AddComponent()
        {
            using (Session.Open(domain))
            {
                using (var transactionScope = Transaction.Open())
                {
                    Component com = new Component()
                    {
                        Name = "Test1"
                    };

                    ComponentCollection.Add(com);

                    transactionScope.Complete();
                }
            }
        }

        static void AddComponentToConfiguration()
        {
            using (Session.Open(domain))
            {
                using (var transactionScope = Transaction.Open())
                {
                    Configuration config = new Configuration()
                    {
                        Name = "Config1"
                    };

                    ComponentCollection.ForEach(c => config.Components.Add(c));

                    transactionScope.Complete();
                }
            }
        }

    }
}

TestListType.Model

using System;
using Xtensive.Storage;

namespace TestListType.Model
{
    [HierarchyRoot]
    public class Component:Entity
    {
        [Field, Key]
        public int Id { get; private set; }

        [Field]
        [Association(PairTo = "Components", OnOwnerRemove = OnRemoveAction.Clear)]
        public Configuration Configuration { get; set; }

        [Field]
        public string Name { get; set; }
    }
 [HierarchyRoot]
    public class Configuration : Entity
    {
        [Field, Key]
        public int Id { get; private set; }

        [Field]
        public string Name { get; set; }

        [Field]
        public EntitySet<Component> Components { get; private set; }
    }
}

If you use the same session for both functions it works fine.

So my question is...how can I, in my case use the same session while using different button_click events to create a "Configuration" item.

Kind regards, Martijn.

This thread was imported from our support forum. The original discussion may contain more detailed answer.

asked Aug 25 '09 at 07:51

Martijnvl's gravatar image

Martijnvl
21555


One Answer:

Alex (Xtensive) wrote:

You're using the correct solution. Since you're modifying persistent objects, you need a transaction, and you open it manually. Btw, there could be auto transactions, but it seems you disabled them for your Session (by default they are disabled, see SessionOptions in Xtensive.Storage.Configuration). On the other hand, you'd get many of them.

Another way to achieve the same is to place all this logic into a method of SessionBound (Entity in particular). If auto transactions are enabled, its execution will be wrapped into a transaction.


Alex (Xtensive) wrote:

What version do you use? I just checked the stack trace: EntitySetBase.Add is transactional method in v4.0.5, but in your case it isn't - a transaction is opened later for Persistent.GetFieldValue. So likely, you're using older version, and there is a bug with this.


otto wrote:

Hi,

I also use 4.0.5, and it works for me like a charm.

Martijn, you could look at this EntitySetBase.Add method in Reflector. It should look something like this:

public bool Add(Entity item)
{
    bool ~returnValue~2;
    object onEntryResult;
    try
    {
        bool CS$1$0000;
        onEntryResult = ~PostSharp~Laos~Implementation.TransactionalAspect~480.OnEntry(this);
        if (this.Contains(item))
        {
            CS$1$0000 = false;
        }
        else
        {
            this.NotifyAdding(item);
            if (this.Field.Association.IsPaired)
            {
                base.Session.PairSyncManager.Enlist(OperationType.Add, this.Owner, item, this.Field.Association);
            }
            if ((this.Field.Association.AuxiliaryType != null) && this.Field.Association.IsMaster)
            {
                this.GetEntitySetTypeState().ItemCtor(item.Key.Value.Combine(this.Owner.Key.Value));
            }
            base.State.Add(item.Key);
            this.NotifyAdd(item);
            CS$1$0000 = true;
        }
        ~returnValue~2 = CS$1$0000;
        ~PostSharp~Laos~Implementation.TransactionalAspect~480.OnSuccess(this, onEntryResult);
    }
    finally
    {
        ~PostSharp~Laos~Implementation.TransactionalAspect~480.OnExit(this, onEntryResult);
    }
    return ~returnValue~2;
}

What happens if you add entities to the entityset that are created in the same transaction?


Alex (Xtensive) wrote:

True... Martijn, can you send us the whole application, or this is not possible?

May be the problem is related to EntitySet events: PairSyncManager is invoked after an Adding event is raised (see NotifyAdding), so if you handle them, can there be some code leading to rollback? But it's strange there is no other exception...


Alex (Xtensive) wrote:

If you're using web application, see our ASP.NET sample (Xtensive.Storage.Samples.AspNet). I strongly recommend to rely on Xtensive.Storage.Web.SessionManager, as it's shown there. Mainly, you should just enable this IHttpModule in Web.config, and default Session.Current will be provided for any web request.

If you're developing WPF application, you should simply share the same Session everywhere. Or at least, in any places where you exchange by some shared entities. This happens in your case - you're trying to use entities created in first session later in second session.

Or, if this can't be achieved, you must exchange e.g. by their Keys, strings representing these Keys (see Key.Parse & Key.Format), or by their native identifiers (e.g. int Id in your case). You can use anything allowing you to identify (i.e. resolve) these objects. But you can't pass any SessionBound object to a different Session "as-is".


Alex (Xtensive) wrote:

Forgot to add: likely, you'll need a method allowing to resolve a bulk of Keys to entities at once. Currently you can do this by two ways:

  • Simple: call Query.Single for each of these Keys

  • Complex: manually build IQueryable containing .Where(e => e.Key==key1 || e.Key==Key2 || ...). Remember about the number of parameters in such a query ;)

Now a good news: we already decided to add Query.Multiple method group in addition to Query.Single. They'll handle exactly this case by the most efficient way. They'll appear quite shortly - either in v4.0.6 (next week) or in v4.0.7 (+ 1-2 weeks more).


Alex (Xtensive) wrote:

v4.1 offers an ideal way of doing this.

See

answered Aug 26 '09 at 07:10

Editor's gravatar image

Editor
46137156157

That's really 4.0.5. We'll check its EntitySetBase.Add MSIL tomorrow. Really strange...

(Aug 26 '09 at 07:10) Alex Yakunin Alex%20Yakunin's gravatar image

Thank you for the reply.

I will wait for the release with Query.Multiple, too see if this fitts my requirements. Will keep you posted regarding my findings.

Kind regards, Martijn

(Aug 26 '09 at 07:10) Martijnvl Martijnvl's gravatar image
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

Subscription:

Once you sign in you will be able to subscribe for any updates here

Tags:

×568

Asked: Aug 25 '09 at 07:51

Seen: 1,854 times

Last updated: Aug 25 '09 at 07:51

powered by OSQA