I have WPF application with simple logic: we have Tasks and Projects entities. Something like this:

class Task
{
    public Project Prj; 
}

On application_start domain and session are created. And on first select query with prefetch (example of code at the end of message) I don't have any problem.

Then, I create new task, select project for it, and call

disconnectedState.ApplyChanges();

Everything is fine: I have new record in database, and I have

task.Prj == null // false

After that I call code with prefetch again. It selects all tasks (and already added one too). But: there is NO Prj in task, which I've already added.

task.Prj == null // true

And trying to access this field throws NullReferenceException.

There is Project.Id in database for this record. When I restart application (rebuild domain) everything works fine. It only happens with new added records.

Example of code:

using (disconnectedState.Connect())
{
    var tasksQuery = Library.Tasks.All()
        .Prefetch(z => z.Project)
        .Prefetch(z => z.User);
    foreach (var task in tasksQuery)
    {
        _tasks.Add(task);

    }
}

Help me, please. Maybe, I don't understand something, but I'm ready to read and learn this :)

asked Nov 08 '10 at 05:53

Ness's gravatar image

Ness
155232328

edited Nov 08 '10 at 06:40

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412


2 Answers:

I tried to reproduce this situation and review my code.

Sorry, there was my bug: one of the events sets Project = null in task object.

So, there is no bug in selecting entities with DisconnectedState.

But now I has another problem: I don't understand behavior of DO in this situation.


//Session is opened at application startup
disconnectedScope.DisposeSafely();
disconnectedState = new DisconnectedState();
disconnectedScope = disconnectedState.Attach(App.Session);

using (disconnectedState.Connect())
{
    var tasksQuery = Query.All<task>();//.Prefetch(z => z.Project);
    foreach (var task in tasksQuery)
    {
        //everything works fine
        var xx = task.Project.Name;

    }
}

Task tempTask;

using (var transactionScope = Transaction.Open())
{
    //Projects - collection of projects selected previously
    var task = new Task() { Project = Projects.FirstOrDefault()};
    task.Name = "xxx";

    tempTask = task;

    transactionScope.Complete();

}
disconnectedState.ApplyChanges();

//now there is record in db

//everything is ok
var pId = tempTask.Project.Id;

//I find similar situation in my code, and link to this object 
// EXISTED in memory, so, I add this line of code to reproduce situation.
// It is bug in my code, but now I don't uderstand such behavior :(
tempTask.Project = null;

//sync = true, but in fact, not (project is set to null, but in db - no)
// Maybe I don't understand behavior of
// PersistenceState
var sync = tempTask.PersistenceState == PersistenceState.Synchronized;

// trying to select all again from db
using (Transaction.Open(App.Session))
{
    using (disconnectedState.Connect())
    {
        var tasksQuery = Query.All<task>(); //.Prefetch(z => z.Project);
        foreach (var task in tasksQuery)
        {
            //NullReferenceException on previously added task "xxx"
            // this entity was'n selected from db, and has Project == null
            var xx = task.Project.Name;

        }
    }
}

Sorry if I missed explanation in the manual.

answered Nov 08 '10 at 08:02

Ness's gravatar image

Ness
155232328

Entity.PeristenceState indicates whether changes were persisted to underlying storage provider. It really doesn't show actual persistence state, if DisconnectedState is attached. Internally DisconnectedState operates as interceptor @ storage provider level, so DO "thinks" it persists everything to actual storage, but actually the data is temporarily cached by DisconnectedState.

Btw, there is no easy way to detect if particular entity was changed after last ApplyChanges or CancelChanges, since DO doesn't need this info. The only option is to study DisconnectedState.Operations content.

(Nov 08 '10 at 08:32) Alex Yakunin Alex%20Yakunin's gravatar image

Ok, thanks.

One more question: how make DisconnectedState to select all entities from db?

In my example it doesn't select Task, because it doesn't replace it's field Project (which is null) and this Task already in memory. But what if somebody changed it in store?

Or I again can't understand something? Please, point me to appropriative manual section.

(Nov 08 '10 at 08:53) Ness Ness's gravatar image
1

If "select" = "(re)fetch", there are two ways:

  • Detach the DisconnectedState and forget about it, attach a new one, and read anything you need to it - completely;
  • Alternatively, you can create a new DisconnectedState, read all you need and Merge it into the old one with MergeMode.PreferNew.
(Nov 08 '10 at 17:20) Alex Yakunin Alex%20Yakunin's gravatar image
1

Note that last option is pretty good candidate for extraction to a separate (extension) method accepting delegate that must refetch the data:

disconnectedState.Refresh(() => {
  Query.All<Task>().Run();
  Query.All<Project>().Run();
});
(Nov 08 '10 at 17:23) Alex Yakunin Alex%20Yakunin's gravatar image

Initially, it looks weird, i.e. probably, that's a result of some bug.

Few questions to help us to reproduce it:

  • Are Task.Project and Project.Task properties paired by some way?
  • In your example, Prj property isn't marked as [Field] - but actually it is?
  • Does removal of .Prefetch change anything? Try to replace .Prefetch with for-loop that simply accesses Project and User properties and see what happens.

answered Nov 08 '10 at 06:45

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412

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

powered by OSQA