Hello everybody.
I'm confused a little :-/
DO is such complex system :)
Here is code. Comments below.
Sorry for huge code. It's simply :)
But there is no another way to show all cases.
// public class Project : Entity
//{
// [Field, Key]
// public int Id { get; private set; }
// [Field]
// public string Name { get; set; }
// [Field]
// public EntitySet<task> Tasks { get; private set; }
// [Field]
// [Association(PairTo = "Projects")]
// public EntitySet<user> Users { get; set; }
//}
// public class User : Entity
//{
// [Field, Key]
// public int Id { get; private set; }
// [Field]
// public string Name { get; set; }
// [Field]
// public EntitySet<project> Projects { get; private set; }
//}
// public class Task : Entity
//{
// [Field, Key]
// public int Id { get; private set; }
// [Field]
// public string Name { get; set; }
// [Field]
// [Association(PairTo = "Tasks")]
// public Project Project { get; set; }
//}
//DS - disconnected state, S - session
Session session = Session.Open(DomainManager.Domain);
var disconnectedState = new DisconnectedState();
using (var disconnectedScope = disconnectedState.Attach(session))
{
using (disconnectedState.Connect())
{
Query.All<project>().Prefetch(z => z.Users).Run();
Query.All<user>().Run();
//all assosiations ok now, it's first time fetching
}
//ONE TO MANY assosiation (Project->Tasks)
var project = new Project { Name = "z1" };
using (disconnectedState.Connect())
{
var task = Query.Single<task>(7);
project.Tasks.Add(task);
var testTask = project.Tasks.FirstOrDefault(u => u.Id == 7); // 1) null
foreach (var taskInner in project.Tasks)
{
var x = taskInner.Id;// 2) x == 7 is true
}
var taskProject = task.Project; // 3) not null
}
// I think, I understand 1) and 2). 1) is Query, so DS goes to S, but there is NO
// new project entity in S, because we haven't applied changes yet.
// 2) - it is only Read (enumerate collection) and 3) so. That's why we have such behavior.
disconnectedState.ApplyChanges();
//now everything ok
using (disconnectedState.Connect())
{
var task = Query.Single<task>(7);
var testTask = project.Tasks.FirstOrDefault(u => u.Id == 7); // 4) not null
foreach (var taskInner in project.Tasks)
{
var x = taskInner.Id;// 5) x == 7 is true
}
var taskProject = task.Project; // 6) not null
}
//MANY TO MANY
var project2 = new Project { Name = "z2" };
using (disconnectedState.Connect())
{
var user = Query.Single<user>(4);
project2.Users.Add(user);
// accessing via project
var uuu = project2.Users.FirstOrDefault(u => u.Id == 4); // 7) null
foreach (var userInner in project2.Users)
{
var x = userInner.Id;// 8) never comes here, enumerates no result
}
// accessing via user
var ppp = user.Projects.FirstOrDefault(u => u.Name == "z2"); // 9) null
foreach (var projectInner in user.Projects)
{
var x = projectInner.Id;// 10) ok, we have Id = -2
}
}
//I can understand it too, I think. Many-to-many - there are entites like User-Projects-Project.
//So, only after calling ApplyChanges such entities will be passed to Session.
//And when I try to enumerate project2.Users — I can't access already created entities User-Projects-Project.
//But I can access user.Projects, because user have been stored in session earlier.
disconnectedState.ApplyChanges();
using (disconnectedState.Connect())
{
var user = Query.Single<user>(4);
// accessing via project
var uuu = project2.Users.FirstOrDefault(u => u.Id == 4); // 11) not null
foreach (var userInner in project2.Users)
{
var x = userInner.Id;// 12) Oops! never comes here, enumerates no result
}
// accessing via user
var ppp = user.Projects.FirstOrDefault(u => u.Name == "z2"); // 9) not null
foreach (var projectInner in user.Projects)
{
var x = projectInner.Id;// 10) ok, we have Id = 19
}
}
//So, 12) confused me :)
}//disposing disconnectedscope of first disconnectedstate
//Creating new disconnected state
var disconnectedState2 = new DisconnectedState();
using (var disconnectedScope = disconnectedState2.Attach(session))
{
using (disconnectedState2.Connect())
{
var project2 = Query.Single<project>(19);//is Id of project with name z2
var user = Query.Single<user>(4);
// accessing via project
var uuu = project2.Users.FirstOrDefault(u => u.Id == 4); // 13) not null
foreach (var userInner in project2.Users)
{
var x = userInner.Id;// 14) ok, Id = 4
}
// accessing via user
var ppp = user.Projects.FirstOrDefault(u => u.Name == "z2"); // 15) not null
foreach (var projectInner in user.Projects)
{
var x = projectInner.Id;// 16) ok, Id = 19
}
//Modifying project
project2.Name = "z2_";
}
disconnectedState2.ApplyChanges();
using (disconnectedState2.Connect())
{
var project2 = Query.Single<project>(19);
var x = project2.Name;//z2_, i.e. saved successfully
}
}//disposing disconnectedscope of second disconnectedstate
//Trying to merge, and read from first disconnectedState
disconnectedState.Merge(disconnectedState2, MergeMode.PreferNew);
using (var disconnectedScope = disconnectedState.Attach(session))
{
var project2 = Query.Single<project>(19);//ok, that ensures, that we use DS cache
var name = project2.Name;//z2_, i.e. merged successfully
using (disconnectedState.Connect())
{
var user = Query.Single<user>(4);
// accessing via project
var uuu = project2.Users.FirstOrDefault(u => u.Id == 4); // 17) not null
foreach (var userInner in project2.Users)
{
var x = userInner.Id;// 18) Oops! never comes here, enumerates no result
}
// accessing via user
var ppp = user.Projects.FirstOrDefault(u => u.Name == "z2_"); // 19) not null
foreach (var projectInner in user.Projects)
{
var x = projectInner.Id;// 20) ok, we have Id = 19
}
}
// 18) confused me again :)
}
Please confirm that I have understood correctly.
And questions:
Why I can't use 12) and 18) ?
If I dispose session and attach DS again - everything is ok. But it that case there is no need in DS, I think. And it seems Merge() merges only fields, not EntitySets.
upd.
We've got magic :)
// public class Project : Entity
//{
// [Field, Key]
// public int Id { get; private set; }
// ....
// [Field]
// public EntitySet<user> Users { get; set; }
//}
// public class User : Entity
//{
// [Field, Key]
// public int Id { get; private set; }
// ...
// [Field]
// [Association(PairTo = "Users")]
// public EntitySet<project> Projects { get; private set; }
//}
If I switch Association PairTo from Project to Users, everything works fine.
12) and 18) enumerates OK.
We'll continue researching.
upd2.
Another case. Changing way of adding association in One-To-Many.
Now we create new task, and associate it with existing project.
// public class Project : Entity
//{
// [Field, Key]
// public int Id { get; private set; }
// [Field]
// public string Name { get; set; }
// [Field]
// public EntitySet<task> Tasks { get; private set; }
// ...
//}
// public class Task : Entity
//{
// [Field, Key]
// public int Id { get; private set; }
// [Field]
// public string Name { get; set; }
// [Field]
// [Association(PairTo = "Tasks")]
// public Project Project { get; set; }
//}
var taskx = new Task { Name = "t1" };
using (disconnectedState.Connect())
{
var projectx = Query.Single<project>(5);
taskx.Project = projectx;
var testTask = projectx.Tasks.FirstOrDefault(u => u.Name == "t1"); // 1) null
foreach (var taskInner in projectx.Tasks)
{
var x = taskInner.Id;// 2) NO results
}
var taskProject = taskx.Project; // 3) not null
}
2) - confusing.
I think that the Association Pair-To strangely works in the DS or I don't understand something.
And note:
if we don't use DS, and repeat this cases in one transaction, everything is ok.
Alex, help me, please :)
upd4.
There are some cases to test.
Context: attached disconnected state. All cases in opened session and transaction
without disconnected state works fine (at least we did not find any problems).
class User
{
[Field]
[Association(PairTo="Users")]
public EntitySet<project> Projects;
}
class Project
{
[Field]
public EntitySet<users> Users;
}
1) create new Project and add existing user to it via project.Users
2) create new Project and it to existing user via user.Projects
3) create new User and add existing project to it via user.Projects
4) create new User and it to existing project via project.Users
5) create new User and new Project and try to add them to each other
via project.Users, user.Projects
6) get existing User and existing Project and try to add them to each other
via project.Users, user.Projects
After every case should check querying and fetching:
user.Projects.FirstOrDefault, project.Users.FirstOrDefault,
enumerate user.Projects and project.Users.
Then we switch PairTo like this:
class User
{
[Field]
public EntitySet<project> Projects;
}
class Project
{
[Field]
[Association(PairTo="Projects")]
public EntitySet<users> Users;
}
And repeat all cases again.
All cases also can be repeated with OneToMany.
Studying this right now, the answer will appear quite soon :)