News

  Analyzed 18 days ago based on code collected 18 days ago.
 
Posted over 3 years ago by nore...@blogger.com (Alex Yakunin)
That's a bit disappointing - not sure, why, but I hoped to see it this year... On the other hand, having a "go live" release (Beta 2) right now is really cool.

So what do you think, should we start porting DO4 to .NET 4.0 \ VS 2010 ... [More] Beta2 right now (actually we thought about this), or it's better to stick to VS.NET 2008 and .NET 3.5 till the new year? [Less]
Posted over 3 years ago by nore...@blogger.com (Alex Yakunin)
Check out:

Introducing Rx (Linq to Events)
Expert to Expert: Brian Beckman and Erik Meijer - Inside the .NET Reactive Framework (Rx)
Kim Hamilton and Wes Dyer: Inside .NET Rx and IObservable/IObserver in the BCL (VS ... [More] 2010)

This is really interesting and impressive! It seems Erik Meijer and the team at Microsoft Research have shown up the next great creation after LINQ. [Less]
Posted over 3 years ago by nore...@blogger.com (Alex Yakunin)
If you don't know about DisconnectedState, please read first section of this post first.

Here is what we're going to deliver:
var keys = new List<Key>();
DisconnectedState state;

using (var session = ... [More] Session.OpenDisconnected(domain)) {
// Validation is off here by default,
// so next line passes even if it violates validation rules
var p = new Person { Name = "Alex", Age = -1 }

// DisconnectedState manages its own local transactions
// and implements unit of work pattern.
try {
() => {
p.Age = 1;
throw new Exception();
}.InvokeTransactionally(); // If you don't know,
// that's the way to create and invoke anonymous
// transactional method.
// See TransactionalExceptions type.
}
catch {}
Assert.AreEqual(-1, p.Age); // Passes.

var prefetchingQuery =
Query<Customer>.All.Where(...)
.Prefetch(c => c.Orders);

// This code will run in single local and single
// database transaction. All the fetched instances
// will be cached in DisconnectedState.
for (customer c in Query<Customer>.All) {
c.Name = string.Format("{0} ({1})", // Local change
c.Name, c.Orders.Count);
keys.Add(c.Key);
}

// Nothing is changed in the database yet -
// all the changes are cached only in DisconnectedState.

// Let's detach the state
state = session.DisconnectedState;
}

// And attach it to a new Session
using (var session = Session.OpenDisconnected(domain, state)) {
// This loop won't hit the database,
// since everything it needs is already there
for (customer c in keys.Select(Query<Customer>.Single(key)) {
Console.WriteLine("Customer: {0}", c.Name);
for (order o in c.Orders)
Console.WriteLine(" Order: {0}", o.ToString());
}

// Let's accept changes now.
// They'll be persisted in a single transaction;
// version checks and validation will be performed
// for all the changed objects.
state.AcceptChanges();
}

It will be possible to extract the changes and apply them separately:

var disconnectedState = ...
var session = ...
// Let's create a change set containing both changes
// and original versions of all the affected objects.
var changeSet = disconnectedState.GetChangeSet();
// Here we clone it by serialization,
// see Xtensive.Core.Helpers.Cloner
var changeSetClone = Cloner.Default.Clone(changeSet);
// And finally, we apply the changes to
// another Session.
changeSetClone.Apply(session);

Initially DisconnectedState will track:

Entity creation, change and removal
Similar EntitySet operations.

So when you call ApplyChanges method (btw, it's possible to pass another Session there), change set is actually applied as sequence of Entity and EntitySet method invocations on objects from this Session. In future it will be possible to replicate top-level SessionBound method calls instead of low-level changed, that must bring true action sequence replication there.

It will be possible to:

RejectChanges. This won't affect any fetched information.
Prevent any transparent fetches. By default they're on - i.e. such a Session works in semi-connected mode, but you can restrict them to be available in special code blocks only (I'm not fully sure about final syntax of this, my current best option is () => { ... }.InvokeConnected() ).
Serialize DisconnectedState and deserialize it. You won't need to create Session and Domain objects on the client to make this working (in this case we will provide "default" domain resembling the structure of original Domain object; Session there will also be "closed" - it won't allow to transparently fetch anything there).

You'll see this feature working in a week or two. I'd be glad to hear any comments. [Less]
Posted over 3 years ago by Dmitri Maximov
Posted over 3 years ago by nore...@blogger.com (Alex Yakunin)
Unfortunately, we identified a set of serious issues in DataObjects.Net v4.1 RC:

CopyIndirectDependencies.targets packed there contains a bug leading to an error on building "Rebuild" target, if there are any project dependencies in the ... [More] project it is imported from (i.e. there are references to projects, but not just to .dlls). So "Rebuild" in VS.NET does not work in this case. Imagine, we've been using this .targets file for about a month, and noticed this just on this week - it seems none of us uses Rebuid, since Build works perfectly with our projects ;) Alexey Kofman said he faced an issue with Rebuild before, but being in hurry, we didn't pay appropriate attention to this.
Our new DataObjects.Net.targets fail if any version of PostSharp is installed on your machine. I mentioned it should be uninstalled, but we understood that people does not pay attention to this. Moreover, it's quite bad if DO4 conflicts with installed PostSharp, especially taking into account the fact that a bit earlier we require you to install it.
Since now we copy all .pdb files into bin folder, you may get a set of breaks on exceptions thrown and caught inside Xtensive.Core during Domain shartup. This is normal - we never use exceptions in normal operation flow, but in some cases .NET does not leave us a choice. E.g. when AssociateProvider tries to instantiate candidate generics, it's impossible to make .NET to notify us this is not possible without an exception (e.g. if "where T: ..." condition fails for a particular generic). So we catch it and try the next one in sequence - that's what AssociateProvider implies. Implementing all these checks manually is pretty complex, so we don't do this for now. But it's not a good idea to copy .pdb and make VS.NET to show all of them as well.
A debug code writing assemblies with built Tuple types is not commented out in Xtensive.Core project sources. So Xtensive.Core tries to create .dll files in bin folder, and if the process had no write permission there, it fails.

Fortunately, there were no any serious issues related to runtime behavior - continuous integration and testing we run almost immediately exposes them. As you see, the issues I just mentioned are related to build and installation process, which isn't covered by our test suite. That's why we got them...

Ok, now the good news:

All these issues are already resolved
New v4.1 installer will be published today.

I'm really sorry for this incident. We'll identify all the persons that downloaded v4.1 RC and notify them personally - likely, this build was a real disappointment for many of them. [Less]
Posted over 3 years ago by nore...@blogger.com (Alex Yakunin)
I just updated v4.1 installer. A bug related to output in Console sample is fixed there; new integrated logger is there as well.
Posted over 3 years ago by nore...@blogger.com (Alex Yakunin)
Initially I planned to talk about future queries here, but since almost all space here takes produced log, I decided to mention it as well.

Note: If you're viewing this post in RSS/Atom reader, open its origin to see it with ... [More] syntax highlighting.

Let's start right from code. If you're interested in conclusions, go to the tail of this post.

Program.cs:
using System;
using System.Linq;
using Xtensive.Storage;
using Xtensive.Storage.Configuration;
using Batching.Model;

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

[Field(Length = 100)]
public string Text { get; set; }
}
}

namespace Batching
{
class Program
{
static void Main(string[] args)
{
var config = DomainConfiguration.Load("Default");
var domain = Domain.Build(config);

using (Session.Open(domain)) {
using (var transactionScope = Transaction.Open()) {
var entity1 = new MyEntity {
Text = "Entity 1"
}; // Nothing is sent to server yet

var entity2 = new MyEntity {
Text = "Entity 2"
}; // Nothing is sent to server yet

var futureCount = Query.ExecuteFutureScalar(
() => Query<MyEntity>.All.Count());

foreach (var e in Query<MyEntity>.All) // Batch is sent
Console.WriteLine("Entity.Text: {0}", e.Text);
Console.WriteLine("Count: {0}", futureCount.Value);

// Committing transaction
transactionScope.Complete();
}
}
Console.ReadLine();
}
}
}

App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="Xtensive.Storage"
type="Xtensive.Storage.Configuration.Elements.ConfigurationSection, Xtensive.Storage" />
<section name="Xtensive.Core.Diagnostics"
type="Xtensive.Core.Diagnostics.Configuration.ConfigurationSection, Xtensive.Core" />
</configSections>
<Xtensive.Core.Diagnostics>
<logs>
<log name="" provider="Null" />
<log name="Storage" provider="File" formatString="{5}"/>
</logs>
</Xtensive.Core.Diagnostics>
<Xtensive.Storage>
<domains>
<domain name="Default" upgradeMode="Recreate" connectionUrl="sqlserver://localhost/DO40-Tests" >
<types>
<add assembly="Batching" />
</types>
</domain>
</domains>
</Xtensive.Storage>
</configuration>

Output:
Entity.Text: Entity 1
Entity.Text: Entity 2
Count: 2

And finally, its log - Batching.exe.log:
Building Domain: started.
Creating Domain: started.
Creating Domain: completed.
Creating HandlerFactory: started.
Creating HandlerFactory: completed.
Creating DomainHandler: started.
Creating DomainHandler: completed.
Creating NameBuilder: started.
Creating NameBuilder: completed.
Creating DomainHandler: started.
Creating DomainHandler: completed.
Building Model: started.
Building Model Definition: started.
Defining 'Types': started.
Defining 'Xtensive.Storage.IEntity': started.
Defining 'Xtensive.Storage.IEntity': completed.
Defining 'Xtensive.Storage.Entity': started.
Field: 'TypeId'
Defining 'Xtensive.Storage.Entity': completed.
Defining 'Xtensive.Storage.Metadata.MetadataBase': started.
Defining 'Xtensive.Storage.Metadata.MetadataBase': completed.
Defining 'Xtensive.Storage.Metadata.Type': started.
Field: 'Id'
Field: 'Name'
Field: 'TypeId'
Index: 'IX_Name'
Hierarchy: 'Type'
Defining 'Xtensive.Storage.Metadata.Type': completed.
Defining 'Xtensive.Storage.Metadata.Extension': started.
Field: 'Name'
Field: 'Text'
Field: 'Data'
Field: 'TypeId'
Hierarchy: 'Extension'
Defining 'Xtensive.Storage.Metadata.Extension': completed.
Defining 'Xtensive.Storage.Structure': started.
Defining 'Xtensive.Storage.Structure': completed.
Defining 'Xtensive.Storage.Metadata.Assembly': started.
Field: 'Name'
Field: 'Version'
Field: 'TypeId'
Hierarchy: 'Assembly'
Defining 'Xtensive.Storage.Metadata.Assembly': completed.
Defining 'Batching.Model.MyEntity': started.
Field: 'Id'
Field: 'Text'
Field: 'TypeId'
Hierarchy: 'MyEntity'
Defining 'Batching.Model.MyEntity': completed.
Defining 'Types': completed.
Building Model Definition: completed.
Building Custom Definitions: started.
Building Custom Definitions: completed.
Inspecting model definition: started.
Inspecting hierarchy 'Type'
Inspecting hierarchy 'Extension'
Inspecting hierarchy 'Assembly'
Inspecting hierarchy 'MyEntity'
Inspecting type 'Entity'
Inspecting type 'MetadataBase'
Inspecting type 'Type'
Inspecting type 'Extension'
Inspecting type 'Structure'
Inspecting type 'Assembly'
Inspecting type 'MyEntity'
Inspecting model definition: completed.
Processing fixup actions: started.
Executing action: 'Mark 'Type.TypeId' field as system.'
Executing action: 'Reorder fields in 'Type' type.'
Executing action: 'Add primary index to 'Type''
Executing action: 'Mark 'Extension.TypeId' field as system.'
Executing action: 'Reorder fields in 'Extension' type.'
Executing action: 'Mark 'Extension.Name' field as not nullable.'
Executing action: 'Add primary index to 'Extension''
Executing action: 'Mark 'Assembly.TypeId' field as system.'
Executing action: 'Reorder fields in 'Assembly' type.'
Executing action: 'Mark 'Assembly.Name' field as not nullable.'
Executing action: 'Add primary index to 'Assembly''
Executing action: 'Mark 'MyEntity.TypeId' field as system.'
Executing action: 'Reorder fields in 'MyEntity' type.'
Executing action: 'Add primary index to 'MyEntity''
Executing action: 'Remove 'Entity' type.'
Executing action: 'Remove 'MetadataBase' type.'
Processing fixup actions: completed.
Building Actual Model: started.
Building Types: started.
Building Type: started.
Building declared field 'Type.Id'
Building Extension: started.
Building declared field 'Extension.Name'
Building Assembly: started.
Building declared field 'Assembly.Name'
Building MyEntity: started.
Building declared field 'MyEntity.Id'
Building Types: completed.
Building Fields: started.
Building declared field 'Type.TypeId'
Building declared field 'Type.Name'
Building declared field 'Extension.TypeId'
Building declared field 'Extension.Text'
Building declared field 'Extension.Data'
Building declared field 'Assembly.TypeId'
Building declared field 'Assembly.Version'
Building declared field 'MyEntity.TypeId'
Building declared field 'MyEntity.Text'
Building Fields: completed.
Building Associations: started.
Building Associations: completed.
Building Indexes: started.
Building index 'IX_Name'
Building index 'PK_Type'
Building index 'PK_Extension'
Building index 'PK_Assembly'
Building index 'PK_MyEntity'
Building Indexes: completed.
Building Actual Model: completed.
Building Model: completed.
Opening session 'Name = System, Options = Default, CacheType = LruWeak, CacheSize = 16384, DefaultIsolationLevel = ReadCommitted'.
Session 'System, #1'. Creating connection 'sqlserver://localhost/DO40-Tests'.
Session 'System, #1'. Opening connection 'sqlserver://localhost/DO40-Tests'.
Session 'System, #1'. Beginning transaction @ ReadCommitted.
Synchronizing schema in Recreate mode: started.
Target schema:
Extracted schema:
Clearing comparison result:
Status: TargetIsSubset, Can't upgrade safely
Hints:

Difference:
"./" != "./" (NodeDifference): 0
+Tables: "./Tables (4)" != "./Tables (0)" (NodeCollectionDifference): 4 change(s)
"./Tables/Metadata.Type" != "" (NodeDifference): Removed
+Columns: "./Tables/Metadata.Type/Columns (3)" != "" (NodeCollectionDifference): 3 change(s)
"./Tables/Metadata.Type/Columns/Id" != "" (NodeDifference): Removed
+Type: "Type: Int32, Length: null" != "" (ValueDifference): values differ
"./Tables/Metadata.Type/Columns/TypeId" != "" (NodeDifference): Removed
+Type: "Type: Int32, Length: null" != "" (ValueDifference): values differ
"./Tables/Metadata.Type/Columns/Name" != "" (NodeDifference): Removed
+Type: "Type: String?, Length: 1000" != "" (ValueDifference): values differ
+PrimaryIndex: "./Tables/Metadata.Type/PrimaryIndex (PK_Type)" != "" (NodeDifference): Removed
+IsUnique: "True" != "False" (ValueDifference): values differ
+KeyColumns: "./Tables/Metadata.Type/PrimaryIndex/KeyColumns (1)" != "" (NodeCollectionDifference): 1 change(s)
"./Tables/Metadata.Type/PrimaryIndex/KeyColumns/0" != "" (NodeDifference): Removed
+Direction: "Positive" != "None" (ValueDifference): values differ
+Value: "./Tables/Metadata.Type/Columns/Id" != "" (ValueDifference): values differ
+SecondaryIndexes: "./Tables/Metadata.Type/SecondaryIndexes (1)" != "" (NodeCollectionDifference): 1 change(s)
"./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name" != "" (NodeDifference): Removed
+IsUnique: "True" != "False" (ValueDifference): values differ
+KeyColumns: "./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/KeyColumns (1)" != "" (NodeCollectionDifference): 1 change(s)
"./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/KeyColumns/0" != "" (NodeDifference): Removed
+Direction: "Positive" != "None" (ValueDifference): values differ
+Value: "./Tables/Metadata.Type/Columns/Name" != "" (ValueDifference): values differ
+PrimaryKeyColumns: "./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/PrimaryKeyColumns (1)" != "" (NodeCollectionDifference): 1 change(s)
"./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/PrimaryKeyColumns/0" != "" (NodeDifference): Removed
+Direction: "Positive" != "None" (ValueDifference): values differ
+Value: "./Tables/Metadata.Type/Columns/Id" != "" (ValueDifference): values differ
+IncludedColumns: "./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/ValueColumns (1)" != "" (NodeCollectionDifference): 1 change(s)
"./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/IncludedColumns/0" != "" (NodeDifference): Removed
+Value: "./Tables/Metadata.Type/Columns/TypeId" != "" (ValueDifference): values differ
"./Tables/Metadata.Extension" != "" (NodeDifference): Removed
+Columns: "./Tables/Metadata.Extension/Columns (4)" != "" (NodeCollectionDifference): 4 change(s)
"./Tables/Metadata.Extension/Columns/Name" != "" (NodeDifference): Removed
+Type: "Type: String, Length: 1024" != "" (ValueDifference): values differ
"./Tables/Metadata.Extension/Columns/TypeId" != "" (NodeDifference): Removed
+Type: "Type: Int32, Length: null" != "" (ValueDifference): values differ
"./Tables/Metadata.Extension/Columns/Text" != "" (NodeDifference): Removed
+Type: "Type: String?, Length: null" != "" (ValueDifference): values differ
"./Tables/Metadata.Extension/Columns/Data" != "" (NodeDifference): Removed
+Type: "Type: Byte[]?, Length: null" != "" (ValueDifference): values differ
+PrimaryIndex: "./Tables/Metadata.Extension/PrimaryIndex (PK_Extension)" != "" (NodeDifference): Removed
+IsUnique: "True" != "False" (ValueDifference): values differ
+KeyColumns: "./Tables/Metadata.Extension/PrimaryIndex/KeyColumns (1)" != "" (NodeCollectionDifference): 1 change(s)
"./Tables/Metadata.Extension/PrimaryIndex/KeyColumns/0" != "" (NodeDifference): Removed
+Direction: "Positive" != "None" (ValueDifference): values differ
+Value: "./Tables/Metadata.Extension/Columns/Name" != "" (ValueDifference): values differ
"./Tables/Metadata.Assembly" != "" (NodeDifference): Removed
+Columns: "./Tables/Metadata.Assembly/Columns (3)" != "" (NodeCollectionDifference): 3 change(s)
"./Tables/Metadata.Assembly/Columns/Name" != "" (NodeDifference): Removed
+Type: "Type: String, Length: 1024" != "" (ValueDifference): values differ
"./Tables/Metadata.Assembly/Columns/TypeId" != "" (NodeDifference): Removed
+Type: "Type: Int32, Length: null" != "" (ValueDifference): values differ
"./Tables/Metadata.Assembly/Columns/Version" != "" (NodeDifference): Removed
+Type: "Type: String?, Length: 64" != "" (ValueDifference): values differ
+PrimaryIndex: "./Tables/Metadata.Assembly/PrimaryIndex (PK_Assembly)" != "" (NodeDifference): Removed
+IsUnique: "True" != "False" (ValueDifference): values differ
+KeyColumns: "./Tables/Metadata.Assembly/PrimaryIndex/KeyColumns (1)" != "" (NodeCollectionDifference): 1 change(s)
"./Tables/Metadata.Assembly/PrimaryIndex/KeyColumns/0" != "" (NodeDifference): Removed
+Direction: "Positive" != "None" (ValueDifference): values differ
+Value: "./Tables/Metadata.Assembly/Columns/Name" != "" (ValueDifference): values differ
"./Tables/MyEntity" != "" (NodeDifference): Removed
+Columns: "./Tables/MyEntity/Columns (3)" != "" (NodeCollectionDifference): 3 change(s)
"./Tables/MyEntity/Columns/Id" != "" (NodeDifference): Removed
+Type: "Type: Int32, Length: null" != "" (ValueDifference): values differ
"./Tables/MyEntity/Columns/TypeId" != "" (NodeDifference): Removed
+Type: "Type: Int32, Length: null" != "" (ValueDifference): values differ
"./Tables/MyEntity/Columns/Text" != "" (NodeDifference): Removed
+Type: "Type: String?, Length: 100" != "" (ValueDifference): values differ
+PrimaryIndex: "./Tables/MyEntity/PrimaryIndex (PK_MyEntity)" != "" (NodeDifference): Removed
+IsUnique: "True" != "False" (ValueDifference): values differ
+KeyColumns: "./Tables/MyEntity/PrimaryIndex/KeyColumns (1)" != "" (NodeCollectionDifference): 1 change(s)
"./Tables/MyEntity/PrimaryIndex/KeyColumns/0" != "" (NodeDifference): Removed
+Direction: "Positive" != "None" (ValueDifference): values differ
+Value: "./Tables/MyEntity/Columns/Id" != "" (ValueDifference): values differ
+Sequences: "./Sequences (1)" != "./Sequences (0)" (NodeCollectionDifference): 1 change(s)
"./Sequences/Int32-Generator" != "" (NodeDifference): Removed
+Increment: "128" != "0" (ValueDifference): values differ
Actions:
GroupingNode, Comment=Prepare
GroupingNode, Comment=.
GroupingNode, Comment=Sequences[]
GroupingNode, Comment=Int32-Generator
RemoveNode, Path=Sequences/Int32-Generator
GroupingNode, Comment=Tables[]
GroupingNode, Comment=Metadata.Type
RemoveNode, Path=Tables/Metadata.Type
GroupingNode, Comment=Metadata.Extension
RemoveNode, Path=Tables/Metadata.Extension
GroupingNode, Comment=Metadata.Assembly
RemoveNode, Path=Tables/Metadata.Assembly
GroupingNode, Comment=MyEntity
RemoveNode, Path=Tables/MyEntity
GroupingNode, Comment=TemporaryRename

Session 'System, #1'. Schema upgrade script:
DROP TABLE [dbo].[Int32-Generator];
DROP TABLE [dbo].[Metadata.Type];
DROP TABLE [dbo].[Metadata.Extension];
DROP TABLE [dbo].[Metadata.Assembly];
DROP TABLE [dbo].[MyEntity];
Session 'n/a'. SQL batch:
DROP TABLE [dbo].[Int32-Generator];
DROP TABLE [dbo].[Metadata.Type];
DROP TABLE [dbo].[Metadata.Extension];
DROP TABLE [dbo].[Metadata.Assembly];
DROP TABLE [dbo].[MyEntity];
Comparison result:
Status: TargetIsSuperset, Can upgrade safely
Hints:

Difference:
"./" != "./" (NodeDifference): 0
+Tables: "./Tables (0)" != "./Tables (4)" (NodeCollectionDifference): 4 change(s)
"" != "./Tables/Metadata.Type" (NodeDifference): Created
+Columns: "" != "./Tables/Metadata.Type/Columns (3)" (NodeCollectionDifference): 3 change(s)
"" != "./Tables/Metadata.Type/Columns/Id" (NodeDifference): Created
+Type: "" != "Type: Int32, Length: null" (ValueDifference): values differ
"" != "./Tables/Metadata.Type/Columns/TypeId" (NodeDifference): Created
+Type: "" != "Type: Int32, Length: null" (ValueDifference): values differ
"" != "./Tables/Metadata.Type/Columns/Name" (NodeDifference): Created
+Type: "" != "Type: String?, Length: 1000" (ValueDifference): values differ
+PrimaryIndex: "" != "./Tables/Metadata.Type/PrimaryIndex (PK_Type)" (NodeDifference): Created
+IsUnique: "False" != "True" (ValueDifference): values differ
+KeyColumns: "" != "./Tables/Metadata.Type/PrimaryIndex/KeyColumns (1)" (NodeCollectionDifference): 1 change(s)
"" != "./Tables/Metadata.Type/PrimaryIndex/KeyColumns/0" (NodeDifference): Created
+Direction: "None" != "Positive" (ValueDifference): values differ
+Value: "" != "./Tables/Metadata.Type/Columns/Id" (ValueDifference): values differ
+SecondaryIndexes: "" != "./Tables/Metadata.Type/SecondaryIndexes (1)" (NodeCollectionDifference): 1 change(s)
"" != "./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name" (NodeDifference): Created
+IsUnique: "False" != "True" (ValueDifference): values differ
+KeyColumns: "" != "./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/KeyColumns (1)" (NodeCollectionDifference): 1 change(s)
"" != "./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/KeyColumns/0" (NodeDifference): Created
+Direction: "None" != "Positive" (ValueDifference): values differ
+Value: "" != "./Tables/Metadata.Type/Columns/Name" (ValueDifference): values differ
+PrimaryKeyColumns: "" != "./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/PrimaryKeyColumns (1)" (NodeCollectionDifference): 1 change(s)
"" != "./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/PrimaryKeyColumns/0" (NodeDifference): Created
+Direction: "None" != "Positive" (ValueDifference): values differ
+Value: "" != "./Tables/Metadata.Type/Columns/Id" (ValueDifference): values differ
+IncludedColumns: "" != "./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/ValueColumns (1)" (NodeCollectionDifference): 1 change(s)
"" != "./Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/IncludedColumns/0" (NodeDifference): Created
+Value: "" != "./Tables/Metadata.Type/Columns/TypeId" (ValueDifference): values differ
"" != "./Tables/Metadata.Extension" (NodeDifference): Created
+Columns: "" != "./Tables/Metadata.Extension/Columns (4)" (NodeCollectionDifference): 4 change(s)
"" != "./Tables/Metadata.Extension/Columns/Name" (NodeDifference): Created
+Type: "" != "Type: String, Length: 1024" (ValueDifference): values differ
"" != "./Tables/Metadata.Extension/Columns/TypeId" (NodeDifference): Created
+Type: "" != "Type: Int32, Length: null" (ValueDifference): values differ
"" != "./Tables/Metadata.Extension/Columns/Text" (NodeDifference): Created
+Type: "" != "Type: String?, Length: null" (ValueDifference): values differ
"" != "./Tables/Metadata.Extension/Columns/Data" (NodeDifference): Created
+Type: "" != "Type: Byte[]?, Length: null" (ValueDifference): values differ
+PrimaryIndex: "" != "./Tables/Metadata.Extension/PrimaryIndex (PK_Extension)" (NodeDifference): Created
+IsUnique: "False" != "True" (ValueDifference): values differ
+KeyColumns: "" != "./Tables/Metadata.Extension/PrimaryIndex/KeyColumns (1)" (NodeCollectionDifference): 1 change(s)
"" != "./Tables/Metadata.Extension/PrimaryIndex/KeyColumns/0" (NodeDifference): Created
+Direction: "None" != "Positive" (ValueDifference): values differ
+Value: "" != "./Tables/Metadata.Extension/Columns/Name" (ValueDifference): values differ
"" != "./Tables/Metadata.Assembly" (NodeDifference): Created
+Columns: "" != "./Tables/Metadata.Assembly/Columns (3)" (NodeCollectionDifference): 3 change(s)
"" != "./Tables/Metadata.Assembly/Columns/Name" (NodeDifference): Created
+Type: "" != "Type: String, Length: 1024" (ValueDifference): values differ
"" != "./Tables/Metadata.Assembly/Columns/TypeId" (NodeDifference): Created
+Type: "" != "Type: Int32, Length: null" (ValueDifference): values differ
"" != "./Tables/Metadata.Assembly/Columns/Version" (NodeDifference): Created
+Type: "" != "Type: String?, Length: 64" (ValueDifference): values differ
+PrimaryIndex: "" != "./Tables/Metadata.Assembly/PrimaryIndex (PK_Assembly)" (NodeDifference): Created
+IsUnique: "False" != "True" (ValueDifference): values differ
+KeyColumns: "" != "./Tables/Metadata.Assembly/PrimaryIndex/KeyColumns (1)" (NodeCollectionDifference): 1 change(s)
"" != "./Tables/Metadata.Assembly/PrimaryIndex/KeyColumns/0" (NodeDifference): Created
+Direction: "None" != "Positive" (ValueDifference): values differ
+Value: "" != "./Tables/Metadata.Assembly/Columns/Name" (ValueDifference): values differ
"" != "./Tables/MyEntity" (NodeDifference): Created
+Columns: "" != "./Tables/MyEntity/Columns (3)" (NodeCollectionDifference): 3 change(s)
"" != "./Tables/MyEntity/Columns/Id" (NodeDifference): Created
+Type: "" != "Type: Int32, Length: null" (ValueDifference): values differ
"" != "./Tables/MyEntity/Columns/TypeId" (NodeDifference): Created
+Type: "" != "Type: Int32, Length: null" (ValueDifference): values differ
"" != "./Tables/MyEntity/Columns/Text" (NodeDifference): Created
+Type: "" != "Type: String?, Length: 100" (ValueDifference): values differ
+PrimaryIndex: "" != "./Tables/MyEntity/PrimaryIndex (PK_MyEntity)" (NodeDifference): Created
+IsUnique: "False" != "True" (ValueDifference): values differ
+KeyColumns: "" != "./Tables/MyEntity/PrimaryIndex/KeyColumns (1)" (NodeCollectionDifference): 1 change(s)
"" != "./Tables/MyEntity/PrimaryIndex/KeyColumns/0" (NodeDifference): Created
+Direction: "None" != "Positive" (ValueDifference): values differ
+Value: "" != "./Tables/MyEntity/Columns/Id" (ValueDifference): values differ
+Sequences: "./Sequences (0)" != "./Sequences (1)" (NodeCollectionDifference): 1 change(s)
"" != "./Sequences/Int32-Generator" (NodeDifference): Created
+Increment: "0" != "128" (ValueDifference): values differ
Actions:
GroupingNode, Comment=Prepare
GroupingNode, Comment=TemporaryRename
GroupingNode, Comment=Upgrade
GroupingNode, Comment=.
GroupingNode, Comment=Tables[]
GroupingNode, Comment=Metadata.Type
CreateNode, Path=, Type=TableInfo, Name=Metadata.Type, Index=0
GroupingNode, Comment=Columns[]
GroupingNode, Comment=Id
CreateNode, Path=Tables/Metadata.Type, Type=ColumnInfo, Name=Id, Index=0
PropertyChange, Path=Tables/Metadata.Type/Columns/Id, Type=Type: Int32, Length: null
GroupingNode, Comment=TypeId
CreateNode, Path=Tables/Metadata.Type, Type=ColumnInfo, Name=TypeId, Index=1
PropertyChange, Path=Tables/Metadata.Type/Columns/TypeId, Type=Type: Int32, Length: null
GroupingNode, Comment=Name
CreateNode, Path=Tables/Metadata.Type, Type=ColumnInfo, Name=Name, Index=2
PropertyChange, Path=Tables/Metadata.Type/Columns/Name, Type=Type: String?, Length: 1000
GroupingNode, Comment=PK_Type
CreateNode, Path=Tables/Metadata.Type, Type=PrimaryIndexInfo, Name=PK_Type
PropertyChange, Path=Tables/Metadata.Type/PrimaryIndex, IsUnique=True
GroupingNode, Comment=KeyColumns[]
GroupingNode, Comment=0
CreateNode, Path=Tables/Metadata.Type/PrimaryIndex, Type=KeyColumnRef, Name=0, Index=0
PropertyChange, Path=Tables/Metadata.Type/PrimaryIndex/KeyColumns/0, Value=Tables/Metadata.Type/Columns/Id, Direction=Positive
GroupingNode, Comment=SecondaryIndexes[]
GroupingNode, Comment=Type.IX_Name
CreateNode, Path=Tables/Metadata.Type, Type=SecondaryIndexInfo, Name=Type.IX_Name, Index=0
PropertyChange, Path=Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name, IsUnique=True
GroupingNode, Comment=KeyColumns[]
GroupingNode, Comment=0
CreateNode, Path=Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name, Type=KeyColumnRef, Name=0, Index=0
PropertyChange, Path=Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/KeyColumns/0, Value=Tables/Metadata.Type/Columns/Name, Direction=Positive
GroupingNode, Comment=PrimaryKeyColumns[]
GroupingNode, Comment=0
CreateNode, Path=Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name, Type=PrimaryKeyColumnRef, Name=0, Index=0
PropertyChange, Path=Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/PrimaryKeyColumns/0, Value=Tables/Metadata.Type/Columns/Id, Direction=Positive
GroupingNode, Comment=IncludedColumns[]
GroupingNode, Comment=0
CreateNode, Path=Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name, Type=IncludedColumnRef, Name=0, Index=0
PropertyChange, Path=Tables/Metadata.Type/SecondaryIndexes/Type.IX_Name/IncludedColumns/0, Value=Tables/Metadata.Type/Columns/TypeId
GroupingNode, Comment=Metadata.Extension
CreateNode, Path=, Type=TableInfo, Name=Metadata.Extension, Index=1
GroupingNode, Comment=Columns[]
GroupingNode, Comment=Name
CreateNode, Path=Tables/Metadata.Extension, Type=ColumnInfo, Name=Name, Index=0
PropertyChange, Path=Tables/Metadata.Extension/Columns/Name, Type=Type: String, Length: 1024
GroupingNode, Comment=TypeId
CreateNode, Path=Tables/Metadata.Extension, Type=ColumnInfo, Name=TypeId, Index=1
PropertyChange, Path=Tables/Metadata.Extension/Columns/TypeId, Type=Type: Int32, Length: null
GroupingNode, Comment=Text
CreateNode, Path=Tables/Metadata.Extension, Type=ColumnInfo, Name=Text, Index=2
PropertyChange, Path=Tables/Metadata.Extension/Columns/Text, Type=Type: String?, Length: null
GroupingNode, Comment=Data
CreateNode, Path=Tables/Metadata.Extension, Type=ColumnInfo, Name=Data, Index=3
PropertyChange, Path=Tables/Metadata.Extension/Columns/Data, Type=Type: Byte[]?, Length: null
GroupingNode, Comment=PK_Extension
CreateNode, Path=Tables/Metadata.Extension, Type=PrimaryIndexInfo, Name=PK_Extension
PropertyChange, Path=Tables/Metadata.Extension/PrimaryIndex, IsUnique=True
GroupingNode, Comment=KeyColumns[]
GroupingNode, Comment=0
CreateNode, Path=Tables/Metadata.Extension/PrimaryIndex, Type=KeyColumnRef, Name=0, Index=0
PropertyChange, Path=Tables/Metadata.Extension/PrimaryIndex/KeyColumns/0, Value=Tables/Metadata.Extension/Columns/Name, Direction=Positive
GroupingNode, Comment=Metadata.Assembly
CreateNode, Path=, Type=TableInfo, Name=Metadata.Assembly, Index=2
GroupingNode, Comment=Columns[]
GroupingNode, Comment=Name
CreateNode, Path=Tables/Metadata.Assembly, Type=ColumnInfo, Name=Name, Index=0
PropertyChange, Path=Tables/Metadata.Assembly/Columns/Name, Type=Type: String, Length: 1024
GroupingNode, Comment=TypeId
CreateNode, Path=Tables/Metadata.Assembly, Type=ColumnInfo, Name=TypeId, Index=1
PropertyChange, Path=Tables/Metadata.Assembly/Columns/TypeId, Type=Type: Int32, Length: null
GroupingNode, Comment=Version
CreateNode, Path=Tables/Metadata.Assembly, Type=ColumnInfo, Name=Version, Index=2
PropertyChange, Path=Tables/Metadata.Assembly/Columns/Version, Type=Type: String?, Length: 64
GroupingNode, Comment=PK_Assembly
CreateNode, Path=Tables/Metadata.Assembly, Type=PrimaryIndexInfo, Name=PK_Assembly
PropertyChange, Path=Tables/Metadata.Assembly/PrimaryIndex, IsUnique=True
GroupingNode, Comment=KeyColumns[]
GroupingNode, Comment=0
CreateNode, Path=Tables/Metadata.Assembly/PrimaryIndex, Type=KeyColumnRef, Name=0, Index=0
PropertyChange, Path=Tables/Metadata.Assembly/PrimaryIndex/KeyColumns/0, Value=Tables/Metadata.Assembly/Columns/Name, Direction=Positive
GroupingNode, Comment=MyEntity
CreateNode, Path=, Type=TableInfo, Name=MyEntity, Index=3
GroupingNode, Comment=Columns[]
GroupingNode, Comment=Id
CreateNode, Path=Tables/MyEntity, Type=ColumnInfo, Name=Id, Index=0
PropertyChange, Path=Tables/MyEntity/Columns/Id, Type=Type: Int32, Length: null
GroupingNode, Comment=TypeId
CreateNode, Path=Tables/MyEntity, Type=ColumnInfo, Name=TypeId, Index=1
PropertyChange, Path=Tables/MyEntity/Columns/TypeId, Type=Type: Int32, Length: null
GroupingNode, Comment=Text
CreateNode, Path=Tables/MyEntity, Type=ColumnInfo, Name=Text, Index=2
PropertyChange, Path=Tables/MyEntity/Columns/Text, Type=Type: String?, Length: 100
GroupingNode, Comment=PK_MyEntity
CreateNode, Path=Tables/MyEntity, Type=PrimaryIndexInfo, Name=PK_MyEntity
PropertyChange, Path=Tables/MyEntity/PrimaryIndex, IsUnique=True
GroupingNode, Comment=KeyColumns[]
GroupingNode, Comment=0
CreateNode, Path=Tables/MyEntity/PrimaryIndex, Type=KeyColumnRef, Name=0, Index=0
PropertyChange, Path=Tables/MyEntity/PrimaryIndex/KeyColumns/0, Value=Tables/MyEntity/Columns/Id, Direction=Positive
GroupingNode, Comment=Sequences[]
GroupingNode, Comment=Int32-Generator
CreateNode, Path=, Type=SequenceInfo, Name=Int32-Generator, Index=0
PropertyChange, Path=Sequences/Int32-Generator, Increment=128
GroupingNode, Comment=DataManipulate
GroupingNode, Comment=Cleanup

Session 'System, #1'. Schema upgrade script:
CREATE TABLE [dbo].[Metadata.Type] ([Id] integer NOT NULL,
[TypeId] integer NOT NULL,
[Name] nvarchar(1000) DEFAULT NULL,
CONSTRAINT [PK_Type] PRIMARY KEY ([Id]));
CREATE UNIQUE INDEX [Type.IX_Name] ON [dbo].[Metadata.Type] ([Name] ASC) INCLUDE ([TypeId]);
CREATE TABLE [dbo].[Metadata.Extension] ([Name] nvarchar(1024) NOT NULL,
[TypeId] integer NOT NULL,
[Text] nvarchar(max) DEFAULT NULL,
[Data] varbinary(max) DEFAULT NULL,
CONSTRAINT [PK_Extension] PRIMARY KEY ([Name]));
CREATE TABLE [dbo].[Metadata.Assembly] ([Name] nvarchar(1024) NOT NULL,
[TypeId] integer NOT NULL,
[Version] nvarchar(64) DEFAULT NULL,
CONSTRAINT [PK_Assembly] PRIMARY KEY ([Name]));
CREATE TABLE [dbo].[MyEntity] ([Id] integer NOT NULL,
[TypeId] integer NOT NULL,
[Text] nvarchar(100) DEFAULT NULL,
CONSTRAINT [PK_MyEntity] PRIMARY KEY ([Id]));
CREATE TABLE [dbo].[Int32-Generator] ([ID] integer IDENTITY (128, 128) NOT NULL);
Session 'n/a'. SQL batch:
CREATE TABLE [dbo].[Metadata.Type] ([Id] integer NOT NULL,
[TypeId] integer NOT NULL,
[Name] nvarchar(1000) DEFAULT NULL,
CONSTRAINT [PK_Type] PRIMARY KEY ([Id]));
CREATE UNIQUE INDEX [Type.IX_Name] ON [dbo].[Metadata.Type] ([Name] ASC) INCLUDE ([TypeId]);
CREATE TABLE [dbo].[Metadata.Extension] ([Name] nvarchar(1024) NOT NULL,
[TypeId] integer NOT NULL,
[Text] nvarchar(max) DEFAULT NULL,
[Data] varbinary(max) DEFAULT NULL,
CONSTRAINT [PK_Extension] PRIMARY KEY ([Name]));
CREATE TABLE [dbo].[Metadata.Assembly] ([Name] nvarchar(1024) NOT NULL,
[TypeId] integer NOT NULL,
[Version] nvarchar(64) DEFAULT NULL,
CONSTRAINT [PK_Assembly] PRIMARY KEY ([Name]));
CREATE TABLE [dbo].[MyEntity] ([Id] integer NOT NULL,
[TypeId] integer NOT NULL,
[Text] nvarchar(100) DEFAULT NULL,
CONSTRAINT [PK_MyEntity] PRIMARY KEY ([Id]));
CREATE TABLE [dbo].[Int32-Generator] ([ID] integer IDENTITY (128, 128) NOT NULL);
Synchronizing schema in Recreate mode: completed.
Creating Generators: started.
Creating Generators: completed.
Session 'System, #1'. SQL batch:
SELECT [a].[Name], [a].[TypeId], [a].[Version] FROM [dbo].[Metadata.Assembly] [a];
Session 'System, #1'. Caching: Key = 'Assembly, (Xtensive.Storage)', Tuple = (Xtensive.Storage, 2, null), State = New.
Session 'System, #1'. Materializing Assembly: Key = 'Assembly, (Xtensive.Storage)'.
Session 'System, #1'. Setting value: Key = 'Assembly, (Xtensive.Storage)', Field = 'Version (FieldInfo)'.
Session 'System, #1'. Getting value: Key = 'Assembly, (Xtensive.Storage)', Field = 'Version (FieldInfo)'.
Session 'System, #1'. Getting value: Key = 'Assembly, (Xtensive.Storage)', Field = 'Name (FieldInfo)'.
Session 'System, #1'. Getting value: Key = 'Assembly, (Xtensive.Storage)', Field = 'Version (FieldInfo)'.
Metadata.Assembly created: 'Xtensive.Storage (Version=1.0.0.0)'.
Session 'System, #1'. Caching: Key = 'Assembly, (Batching)', Tuple = (Batching, 2, null), State = New.
Session 'System, #1'. Materializing Assembly: Key = 'Assembly, (Batching)'.
Session 'System, #1'. Setting value: Key = 'Assembly, (Batching)', Field = 'Version (FieldInfo)'.
Session 'System, #1'. Getting value: Key = 'Assembly, (Batching)', Field = 'Version (FieldInfo)'.
Session 'System, #1'. Getting value: Key = 'Assembly, (Batching)', Field = 'Name (FieldInfo)'.
Session 'System, #1'. Getting value: Key = 'Assembly, (Batching)', Field = 'Version (FieldInfo)'.
Metadata.Assembly created: 'Batching (Version=1.0.0.0)'.
Session 'System, #1'. Persisting (partial)...
Session 'System, #1'. Persist completed.
Session 'System, #1'. SQL batch:
INSERT INTO [dbo].[Metadata.Assembly] ([Name], [TypeId], [Version]) VALUES (@p0_0, @p0_1, @p0_2);
INSERT INTO [dbo].[Metadata.Assembly] ([Name], [TypeId], [Version]) VALUES (@p1_0, @p1_1, @p1_2);
SELECT [a].[Id], [a].[TypeId], [a].[Name] FROM [dbo].[Metadata.Type] [a];
Session 'System, #1'. Caching: Key = 'Type, (1)', Tuple = (1, 1, null), State = New.
Session 'System, #1'. Materializing Type: Key = 'Type, (1)'.
Session 'System, #1'. Setting value: Key = 'Type, (1)', Field = 'Name (FieldInfo)'.
Session 'System, #1'. Getting value: Key = 'Type, (1)', Field = 'Name (FieldInfo)'.
Session 'System, #1'. Caching: Key = 'Type, (3)', Tuple = (3, 1, null), State = New.
Session 'System, #1'. Materializing Type: Key = 'Type, (3)'.
Session 'System, #1'. Setting value: Key = 'Type, (3)', Field = 'Name (FieldInfo)'.
Session 'System, #1'. Getting value: Key = 'Type, (3)', Field = 'Name (FieldInfo)'.
Session 'System, #1'. Caching: Key = 'Type, (2)', Tuple = (2, 1, null), State = New.
Session 'System, #1'. Materializing Type: Key = 'Type, (2)'.
Session 'System, #1'. Setting value: Key = 'Type, (2)', Field = 'Name (FieldInfo)'.
Session 'System, #1'. Getting value: Key = 'Type, (2)', Field = 'Name (FieldInfo)'.
Session 'System, #1'. Caching: Key = 'Type, (101)', Tuple = (101, 1, null), State = New.
Session 'System, #1'. Materializing Type: Key = 'Type, (101)'.
Session 'System, #1'. Setting value: Key = 'Type, (101)', Field = 'Name (FieldInfo)'.
Session 'System, #1'. Getting value: Key = 'Type, (101)', Field = 'Name (FieldInfo)'.
Session 'System, #1'. Persisting (partial)...
Session 'System, #1'. Persist completed.
Session 'System, #1'. SQL batch:
INSERT INTO [dbo].[Metadata.Type] ([Id], [TypeId], [Name]) VALUES (@p0_0, @p0_1, @p0_2);
INSERT INTO [dbo].[Metadata.Type] ([Id], [TypeId], [Name]) VALUES (@p1_0, @p1_1, @p1_2);
INSERT INTO [dbo].[Metadata.Type] ([Id], [TypeId], [Name]) VALUES (@p2_0, @p2_1, @p2_2);
INSERT INTO [dbo].[Metadata.Type] ([Id], [TypeId], [Name]) VALUES (@p3_0, @p3_1, @p3_2);
SELECT TOP 2 [a].[Name], [a].[TypeId], [a].[Text], [a].[Data] FROM [dbo].[Metadata.Extension] [a] WHERE ([a].[Name] = 'Xtensive.Storage.Model') ORDER BY [a].[Name] ASC;
Session 'System, #1'. Caching: Key = 'Extension, (Xtensive.Storage.Model)', Tuple = (Xtensive.Storage.Model, 3, null, null), State = New.
Session 'System, #1'. Materializing Extension: Key = 'Extension, (Xtensive.Storage.Model)'.
Session 'System, #1'. Setting value: Key = 'Extension, (Xtensive.Storage.Model)', Field = 'Text (FieldInfo)'.
Session 'System, #1'. Getting value: Key = 'Extension, (Xtensive.Storage.Model)', Field = 'Text (FieldInfo)'.
Session 'System, #1'. Persisting (full)...
Session 'System, #1'. SQL batch:
INSERT INTO [dbo].[Metadata.Extension] ([Name], [TypeId], [Text], [Data]) VALUES (@p0_0, @p0_1, @p0_2, @p0_3);
Session 'System, #1'. Persist completed.
Session 'System, #1'. Commit transaction.
Session 'System, #1'. Disposing.
Session 'System, #1'. Closing connection 'sqlserver://localhost/DO40-Tests'.
Building Domain: completed.
Opening session 'Name = Default, Options = Default, CacheType = LruWeak, CacheSize = 16384, DefaultIsolationLevel = ReadCommitted'.
Session 'Default, #2'. Creating connection 'sqlserver://localhost/DO40-Tests'.
Session 'Default, #2'. Opening connection 'sqlserver://localhost/DO40-Tests'.
Session 'Default, #2'. Beginning transaction @ ReadCommitted.
Opening session 'Name = KeyGenerator, Options = Default, CacheType = LruWeak, CacheSize = 16384, DefaultIsolationLevel = ReadCommitted'.
Session 'KeyGenerator, #3'. Creating connection 'sqlserver://localhost/DO40-Tests'.
Session 'KeyGenerator, #3'. Opening connection 'sqlserver://localhost/DO40-Tests'.
Session 'KeyGenerator, #3'. Beginning transaction @ ReadCommitted.
Session 'KeyGenerator, #3'. SQL batch:
INSERT INTO [dbo].[Int32-Generator] DEFAULT VALUES;
SELECT SCOPE_IDENTITY();
Session 'KeyGenerator, #3'. Commit transaction.
Session 'KeyGenerator, #3'. Disposing.
Session 'KeyGenerator, #3'. Closing connection 'sqlserver://localhost/DO40-Tests'.
Session 'Default, #2'. Caching: Key = 'MyEntity, (1)', Tuple = (1, 101, null), State = New.
Session 'Default, #2'. Materializing MyEntity: Key = 'MyEntity, (1)'.
Session 'Default, #2'. Setting value: Key = 'MyEntity, (1)', Field = 'Text (FieldInfo)'.
Session 'Default, #2'. Getting value: Key = 'MyEntity, (1)', Field = 'Text (FieldInfo)'.
Session 'Default, #2'. Caching: Key = 'MyEntity, (2)', Tuple = (2, 101, null), State = New.
Session 'Default, #2'. Materializing MyEntity: Key = 'MyEntity, (2)'.
Session 'Default, #2'. Setting value: Key = 'MyEntity, (2)', Field = 'Text (FieldInfo)'.
Session 'Default, #2'. Getting value: Key = 'MyEntity, (2)', Field = 'Text (FieldInfo)'.
Session 'Default, #2'. Persisting (partial)...
Session 'Default, #2'. Persist completed.
Session 'Default, #2'. SQL batch:
INSERT INTO [dbo].[MyEntity] ([Id], [TypeId], [Text]) VALUES (@p0_0, @p0_1, @p0_2);
INSERT INTO [dbo].[MyEntity] ([Id], [TypeId], [Text]) VALUES (@p1_0, @p1_1, @p1_2);
SELECT COUNT_BIG(*) AS [column] FROM [dbo].[MyEntity] [a];
SELECT [a].[Id], [a].[TypeId], [a].[Text] FROM [dbo].[MyEntity] [a];
Session 'Default, #2'. Updating cache: Key = 'MyEntity, (1)', Tuple = (1, 101, Entity 1), State = Synchronized.
Session 'Default, #2'. Getting value: Key = 'MyEntity, (1)', Field = 'Text (FieldInfo)'.
Session 'Default, #2'. Updating cache: Key = 'MyEntity, (2)', Tuple = (2, 101, Entity 2), State = Synchronized.
Session 'Default, #2'. Getting value: Key = 'MyEntity, (2)', Field = 'Text (FieldInfo)'.
Session 'Default, #2'. Commit transaction.
Session 'Default, #2'. Disposing.
Session 'Default, #2'. Closing connection 'sqlserver://localhost/DO40-Tests'.

Really long, but there are lots of interesting things. But for now I'd like to pay your attention to just a single aspect. This log perfectly shows how our uber-batching and future queries work.

First part to pay attention to this batch:
-- Session 'System, #1'. SQL batch:
INSERT INTO [dbo].[Metadata.Type] ([Id], [TypeId], [Name]) VALUES (@p0_0, @p0_1, @p0_2);
INSERT INTO [dbo].[Metadata.Type] ([Id], [TypeId], [Name]) VALUES (@p1_0, @p1_1, @p1_2);
INSERT INTO [dbo].[Metadata.Type] ([Id], [TypeId], [Name]) VALUES (@p2_0, @p2_1, @p2_2);
INSERT INTO [dbo].[Metadata.Type] ([Id], [TypeId], [Name]) VALUES (@p3_0, @p3_1, @p3_2);
SELECT TOP 2 [a].[Name], [a].[TypeId], [a].[Text], [a].[Data] FROM [dbo].[Metadata.Extension] [a] WHERE ([a].[Name] = 'Xtensive.Storage.Model') ORDER BY [a].[Name] ASC;

You may find this batch is produced by the following code from our SystemUpgradeHandler class:
domainModel.Types
.Where(type => type.TypeId!=TypeInfo.NoTypeId)
.Apply(type => new M.Type(type.TypeId, type.UnderlyingType.GetFullName()));

...

var modelHolder = Query<Extension>.All.SingleOrDefault(
extension => extension.Name==WellKnown.DomainModelExtensionName);

So in fact, it executes 5 different commands. So 5 separate roundtrips is nearly what you would get with EF and many other ORM tools. AFAIK, you would see 2 roundtrips in the best case (if CUD sequence batching is implemented). In our case there is just a single roundtrip!

Note how gracefully we handle SingleOrDefault(): our query translator has added TOP 2 (think why 2 :) ) and ORDER BY to make TOP work.

The second batch is formed by code inside using block of Main method of Program class:
-- Session 'Default, #2'. SQL batch:
INSERT INTO [dbo].[MyEntity] ([Id], [TypeId], [Text]) VALUES (@p0_0, @p0_1, @p0_2);
INSERT INTO [dbo].[MyEntity] ([Id], [TypeId], [Text]) VALUES (@p1_0, @p1_1, @p1_2);
SELECT COUNT_BIG(*) AS [column] FROM [dbo].[MyEntity] [a];
SELECT [a].[Id], [a].[TypeId], [a].[Text] FROM [dbo].[MyEntity] [a];

Isn't this beautiful? You make a set of CUD commands and queries, but DO4 runs all of them in a single roundtrip! Normally you shouldn't touch your code at all to get this!

So DO4 reduces chattiness between application server and RDBMS to nearly minimal possible level.

You may find that there are 2 more batches.

First one updates [Metadata.Extension] table: actually it persists serialized storage model there
Second one allocates next bulk of keys from integer key generator (128 at once by default).

Now let's talk a bit about logging. With today's nightly build it is possible to use it without any third-party logger, such as Log4Net. By default it writes everything to Debug output, if debugger is attached to the current process. Note that default log format is more comprehensive - if you completely remove <logs> section (or e.g. remove formatString attribute), you should see nearly the following output:

0,09s/T1 Info Storage.Building Building Domain: started.
0,09s/T1 Info Storage.Building Creating Domain: started.
0,12s/T1 Info Storage.Building Creating Domain: completed.
...
2,57s/T1 Debug Storage.Providers.Sql Session 'KeyGenerator, #3'. Beginning transaction @ ReadCommitted.
2,57s/T1 Debug Storage.Providers.Sql Session 'KeyGenerator, #3'. SQL batch:
INSERT INTO [dbo].[Int32-Generator] DEFAULT VALUES;
SELECT SCOPE_IDENTITY();
2,57s/T1 Debug Storage.Providers.Sql Session 'KeyGenerator, #3'. Commit transaction.
...

So even integrated log providers are very flexible. You can choose:

Which logs (by namespaces they're located in; "Xtensive." prefix is cut out) must be directed to which providers (provider attribute value can be Null, Console, Error, Debug, File; fileName attribute allows to specify file name)
Which event types must be shown there (events attribute allows to do this).
Pre-defined format: format attribute value can be Simple or Comprehensive.
Custom format string: use formatString attribute to set it. Comprehensive format string is "{0,6:F2}s/T{1,-5} {2,5} {3,-24} {5}".

Here is more comprehensive example of logs section:
<!-- Log nothing by default -->
<log name=""
provider="Null" />
<!-- But use these settings for Xtensive.Core.* logs -->
<log name="Core"
provider="Console" events="Warning,Error,FatalError" format="Simple" />
<!-- This overrides settings for Core.Diagnistics.* logs -->
<log name="Core.Diagnistics"
provider="Console" format="{4}{5}" />
<!-- Use these settings for Xtensive.Storage.* logs -->
<log name="Storage"
provider="File" fileName="Storage.log" />

Note: you need today's nightly build to make new logging stuff work. We added such integrated log providers just today.

P.S. Try to study the log - this might explain lots of aspects of our framework. [Less]
Posted over 3 years ago by Dmitri Maximov
Posted over 3 years ago by Dmitri Maximov
Posted over 3 years ago by nore...@blogger.com (Alex Yakunin)
Finally it's done: v4.1 Release Candidate is here.

Official release announcement will be published shortly, as well as roadmap, revision history and issues in issue tracker. I should say we were unable to accomplish the ... [More] following planned tasks:

There is no Manual for now. It will appear on the next week.
DisconnectedState isn't fully operational yet, and thus there is no demo. We hope to finish with this on the next week as well.
Prefetch API works, but queries it sends are far from ideal yet. They'll be ideal when we'll finish with  automatic fall back to IN for local collections. Currently they're always processed via temporary tables. Although new API already improves performance of raw fetches by nearly 3 times (shortly it will be ~ 10 more times faster), and significantly improves multiple EntitySet access performance.
There is no ASP.NET MVC sample yet, but it will be there soon as well.

Everything else works as we promised. There was a real hell with persistent interfaces, and frankly speaking, we spent much more time on this in comparison to our initial estimates.

We also slightly changed our samples:

ASP.NET sample handles concurrent update checks relying on our new Entity.GetVersion() method. To test this, try to edit the same entity from two different pages.
WPF sample got skin and WPF validation support (now we support IDataErrorInfo).

Finally, there is much better installer and integration with MSBuild:

No need to install PostSharp or something else
No more installation into GAC, so your bin folders contain binaries fully ready for deployment with XCopy
We use new way of integration with MSBuild allowing you to make a project based on DO4 fully "XCopyable" - you won't need to install DO4 to compile it on another PC.
There are new project templates - they're much better for beginners
You can copy DO4 to another PC without installer now. Just XCopy it and run Install.bat; Uninstall.bat will remove it.

Notes for v4.X users: 

You must modify your existing .csproj files while switching to this version.
Uninstall all third-party tools installed by previous versions of DO4 before switching to v4.1, including PostSharp.

Go and try it now! [Less]
 

 
 

Creative Commons License Copyright © 2013 Black Duck Software, Inc. and its contributors, Some Rights Reserved. Unless otherwise marked, this work is licensed under a Creative Commons Attribution 3.0 Unported License . Ohloh ® and the Ohloh logo are trademarks of Black Duck Software, Inc. in the United States and/or other jurisdictions. All other trademarks are the property of their respective holders.