You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by dm...@apache.org on 2020/09/29 22:55:13 UTC

[ignite] branch IGNITE-7595 updated: ported all .NET-specific pages from readme.io to the new documentation engine

This is an automated email from the ASF dual-hosted git repository.

dmagda pushed a commit to branch IGNITE-7595
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/IGNITE-7595 by this push:
     new 0dd5605  ported all .NET-specific pages from readme.io to the new documentation engine
0dd5605 is described below

commit 0dd5605de364e068decf3a75c7277c5bdf69f9ad
Author: Denis Magda <dm...@gridgain.com>
AuthorDate: Tue Sep 29 15:54:29 2020 -0700

    ported all .NET-specific pages from readme.io to the new documentation engine
---
 docs/_data/toc.yaml                                |  16 +-
 docs/_docs/SQL/indexes.adoc                        |   8 +-
 docs/_docs/images/net-view-details.png             | Bin 0 -> 56828 bytes
 .../_docs/net-specific/asp-net-output-caching.adoc |  79 ++++++
 .../asp-net-session-state-caching.adoc             |  67 +++++
 .../_docs/net-specific/cross-platform-support.adoc |  51 ++++
 .../_docs/net-specific/entity-framework-cache.adoc | 184 +++++++++++++
 .../net-specific/java-services-execution.adoc      | 102 +++++++
 docs/_docs/net-specific/linq.adoc                  | 242 +++++++++++++++++
 docs/_docs/net-specific/net-logging.adoc           | 119 ++++++++
 docs/_docs/net-specific/net-plugins.adoc           | 155 +++++++++++
 docs/_docs/net-specific/net-serialization.adoc     | 300 +++++++++++++++++++++
 docs/_docs/net-specific/net-troubleshooting.adoc   | 124 +++++++++
 .../net-specific/platform-interoperability.adoc    | 181 +++++++++++++
 .../net-specific/remote-assembly-loading.adoc      | 140 ++++++++++
 15 files changed, 1759 insertions(+), 9 deletions(-)

diff --git a/docs/_data/toc.yaml b/docs/_data/toc.yaml
index 8db5154..c68e4e8 100644
--- a/docs/_data/toc.yaml
+++ b/docs/_data/toc.yaml
@@ -430,15 +430,15 @@
     - title: Standalone Nodes
       url: net-specific/standalone-nodes
     - title: Logging
-      url: net-specific/logging
+      url: net-specific/net-logging
     - title: LINQ
       url: net-specific/linq
     - title: Java Services Execution
       url: net-specific/java-services-execution
     - title: Plugins
-      url: net-specific/plugins
+      url: net-specific/net-plugins
     - title: Serialization
-      url: net-specific/serialization
+      url: net-specific/net-serialization
     - title: Cross-Platform Support
       url: net-specific/cross-platform-support
     - title: Platform Interoperability
@@ -446,9 +446,15 @@
     - title: Remote Assembly Loading
       url: net-specific/remote-assembly-loading
     - title: Troubleshooting
-      url: net-specific/troubleshooting
+      url: net-specific/net-troubleshooting
     - title: Integrations
-      url: net-specific/integrations
+      items:
+        - title: ASP.NET Output Caching
+          url: net-specific/asp-net-output-caching
+        - title: ASP.NET Session State Caching
+          url: net-specific/asp-net-session-state-caching
+        - title: Entity Framework 2nd Level Cache
+          url: net-specific/entity-framework-cache
 - title: Plugins
   url: plugins
 - title: SQL Reference
diff --git a/docs/_docs/SQL/indexes.adoc b/docs/_docs/SQL/indexes.adoc
index 1121d8b..d519bb7 100644
--- a/docs/_docs/SQL/indexes.adoc
+++ b/docs/_docs/SQL/indexes.adoc
@@ -14,6 +14,10 @@ Ignite automatically creates indexes for each primary key and affinity key field
 When you define an index on a field in the value object, Ignite creates a composite index consisting of the indexed field and the cache's primary key.
 In SQL terms, it means that the index will be composed of two columns: the column you want to index and the primary key column.
 
+== Creating Indexes With SQL
+
+Refer to the link:sql-reference/ddl#create-index[CREATE INDEX] section.
+
 == Configuring Indexes Using Annotations
 
 Indexes, as well as queryable fields, can be configured from code via the `@QuerySqlField` annotation. In the example below, the Ignite SQL engine will create indexes for the `id` and `salary` fields.
@@ -218,10 +222,6 @@ include::{javaFile}[tag=query,indent=0]
 Use the link:sql-reference/ddl#create-index[CREATE/DROP INDEX] command if you need to manage indexes or make new fields of the object visible to the SQL engine at​ runtime.
 ====
 
-== Creating Indexes Using SQL Command
-
-Refer to the link:sql-reference/ddl#create-index[CREATE INDEX] section.
-
 == Configuring Index Inline Size
 
 Proper index inline size can help speed up queries on indexed fields.
diff --git a/docs/_docs/images/net-view-details.png b/docs/_docs/images/net-view-details.png
new file mode 100644
index 0000000..b74c945
Binary files /dev/null and b/docs/_docs/images/net-view-details.png differ
diff --git a/docs/_docs/net-specific/asp-net-output-caching.adoc b/docs/_docs/net-specific/asp-net-output-caching.adoc
new file mode 100644
index 0000000..8cc1ce5
--- /dev/null
+++ b/docs/_docs/net-specific/asp-net-output-caching.adoc
@@ -0,0 +1,79 @@
+= ASP.NET Output Caching
+
+== Overview
+
+Ignite cache can be used as an ASP.NET output cache. This can work especially well for web farms where cached output will
+be shared between web servers.
+
+== Installation
+
+* *Binary distribution*: add a reference to `Apache.Ignite.AspNet.dll`
+* *NuGet*: `Install-Package Apache.Ignite.AspNet`
+
+== Launching Ignite Automatically
+
+To start Ignite automatically for output caching, configure it
+link:net-specific/configuration-options#configure-with-application-or-web-config-files[in the web.config file via IgniteConfigurationSection]
+
+[tabs]
+--
+tab:web.config[]
+[source,xml]
+----
+<configuration>
+    <configSections>
+        <section name="igniteConfiguration" type="Apache.Ignite.Core.IgniteConfigurationSection, Apache.Ignite.Core" />
+    </configSections>
+
+    <igniteConfiguration autoGenerateIgniteInstanceName="true">
+        <cacheConfiguration>
+            <cacheConfiguration name='myWebCache' />
+        </cacheConfiguration>
+    </igniteConfiguration>
+</configuration>
+----
+--
+
+Enable the caching in the `web.config` settings:
+
+[tabs]
+--
+tab:web.config[]
+[source,xml]
+----
+<system.web>
+  <caching>
+    <outputCache defaultProvider="apacheIgnite">
+      <providers>
+          <add name="apacheIgnite" type="Apache.Ignite.AspNet.IgniteOutputCacheProvider, Apache.Ignite.AspNet" igniteConfigurationSectionName="igniteConfiguration" cacheName="myWebCache" />
+      </providers>
+    </outputCache>
+  </caching>
+</system.web>
+----
+--
+
+== Launching Ignite Manually
+
+You can start an Ignite instance manually and specify it's name in the provider configuration:
+
+[tabs]
+--
+tab:web.config[]
+[source,xml]
+----
+<system.web>
+  <caching>
+    <outputCache defaultProvider="apacheIgnite">
+      <providers>
+          <add name="apacheIgnite" type="Apache.Ignite.AspNet.IgniteOutputCacheProvider, Apache.Ignite.AspNet" cacheName="myWebCache" />
+      </providers>
+    </outputCache>
+  </caching>
+</system.web>
+----
+--
+
+The Ignite instance needs to be started before any request is served. Typically this is done in the `Application_Start` method of the `global.asax`.
+
+See link:net-specific/deployment-options#asp-net-deployment[ASP.NET Deployment] for web deployment specifics related to the `IGNITE_HOME` variable.
diff --git a/docs/_docs/net-specific/asp-net-session-state-caching.adoc b/docs/_docs/net-specific/asp-net-session-state-caching.adoc
new file mode 100644
index 0000000..b60a5da
--- /dev/null
+++ b/docs/_docs/net-specific/asp-net-session-state-caching.adoc
@@ -0,0 +1,67 @@
+= ASP.NET Session State Caching
+
+== Overview
+
+The ASP.NET session state caching is designed to allow you to store user session data in different sources.
+By default, session state values and information are stored in memory within the ASP.NET process.
+
+Ignite.NET implements a session state store provider that stores session data in a distributed Ignite cluster that spreads
+the session data across multiple servers in order to provide high availability, load balancing and fault tolerance.
+
+[CAUTION]
+====
+[discrete]
+=== Development and Debugging
+During development and debugging, IIS will dynamically detect code changes when you build and run your web application.
+This, however, does not restart the embedded Ignite instance and can cause exceptions and undesired behavior.
+Make sure to restart IIS manually when using the Ignite Session State Cache.
+====
+
+== Installation
+
+* *Binary distribution*: add a reference to Apache.Ignite.AspNet.dll
+* *NuGet*: `Install-Package Apache.Ignite.AspNet`
+
+== Configuration
+
+To enable the Ignite-based session state storage, modify the `web.config` file as follows:
+
+[tabs]
+--
+tab:web.config[]
+[source,xml]
+----
+<system.web>
+  ...
+  <sessionState mode="Custom" customProvider="IgniteSessionStateProvider">
+    <providers>
+      <add name="IgniteSessionStateProvider"
+           type="Apache.Ignite.AspNet.IgniteSessionStateStoreProvider, Apache.Ignite.AspNet"
+           igniteConfigurationSectionName="igniteConfiguration"
+           applicationId="myApp"
+           gridName="myGrid"
+           cacheName="aspNetSessionCache" />
+    </providers>
+  </sessionState>
+  ...
+</<system.web>
+----
+--
+
+While the `name` and `type` attributes are required, the other attributes listed below are optional:
+
+[cols="1,3",opts="header"]
+|===
+|Attribute |Description
+|`igniteConfigurationSectionName`| The `web.config` section name defined in `configSections`. See
+link:http://127.0.0.1:4000/docs/net-specific/configuration-options#configure-with-application-or-web-config-files[Configuration: web.config] for
+more details. This configuration will be used to start Ignite if it is not started yet.
+|`applicationId`| Should only be used when multiple web applications share the same Ignite session state cache. Assign
+different ID strings to avoid session data conflicts between applications. It is recommended to use a separate cache
+for each application via `cacheName` attribute.
+|`gridName`| Session state provider calls `Ignition.TryGetIgnite` with this grid name to check whether Ignite is already started.
+|`cacheName`| Session state cache name. Default is `ASPNET_SESSION_STATE`.
+|===
+
+For more details on how to start Ignite within an ASP.NET application, refer to link:net-specific/asp-net-output-caching[ASP.NET Output Caching].
+Also, see link:net-specific/deployment-options#asp-net-deployment[ASP.NET Deployment] for web deployment specifics related to the `IGNITE_HOME` variable.
diff --git a/docs/_docs/net-specific/cross-platform-support.adoc b/docs/_docs/net-specific/cross-platform-support.adoc
new file mode 100644
index 0000000..49b895b
--- /dev/null
+++ b/docs/_docs/net-specific/cross-platform-support.adoc
@@ -0,0 +1,51 @@
+= Cross-Platform Support
+
+== Overview
+
+Starting with version 2.4, Ignite.NET supports .NET Core. It is possible to run .NET nodes and develop Ignite.NET
+applications for Linux and macOS, as well as Windows.
+
+== .NET Core
+
+*Requirements:*
+
+* https://www.microsoft.com/net/download/[.NET Core SDK 2.0+, window=_blank]
+* http://www.oracle.com/technetwork/java/javase/downloads/index.html[Java 8+, window=_blank] (macOS requires JDK, otherwise JRE works)
+
+*Running Examples*
+https://ignite.apache.org/download.cgi#binaries[Binary distribution, window=_blank] includes .NET Core examples:
+
+* Download https://ignite.apache.org/download.cgi#binaries[binary distribution, window=_blank] from the Ignite website and extract into any directory.
+* `cd platforms/dotnet/examples/dotnetcore`
+* `dotnet run`
+
+== Java Detection
+
+Ignite.NET looks for a Java installation directory in the following places:
+
+* `HKLM\Software\JavaSoft\Java Runtime Environment` (Windows)
+* `/usr/bin/java` (Linux)
+* `/Library/Java/JavaVirtualMachines` (macOS)
+
+If you changed the default location of Java, then specify the actual path using one of the methods below:
+
+* Set the `IgniteConfiguration.JvmDllPath` property
+* or set the `JAVA_HOME` environment variable
+
+== Known Issues
+
+*No Java runtime present, requesting install*
+
+Java `8u151` has a known bug on macOS: https://bugs.openjdk.java.net/browse/JDK-7131356[JDK-7131356, window=_blank]. Make sure to install `8u152` or later.
+
+*Serializing delegates is not supported on this platform*
+
+.NET Core does not support delegate serialization, `System.MulticastDelegate.GetObjectData`
+just https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/MulticastDelegate.cs#L52[throws an exception, window=_blank],
+so Ignite.NET can not serialize delegates or objects containing them.
+
+*Could not load file or assembly 'System.Configuration.ConfigurationManager'*
+
+Known https://github.com/dotnet/standard/issues/506[.NET issue (506), window=_blank], in some cases additional package reference is required:
+
+* `dotnet add package System.Configuration.ConfigurationManager`
diff --git a/docs/_docs/net-specific/entity-framework-cache.adoc b/docs/_docs/net-specific/entity-framework-cache.adoc
new file mode 100644
index 0000000..67fe3f6
--- /dev/null
+++ b/docs/_docs/net-specific/entity-framework-cache.adoc
@@ -0,0 +1,184 @@
+= Entity Framework 2nd Level Cache
+
+== Overview
+
+Entity Framework, as most other ORMs, can use caching on multiple levels.
+
+* First level caching is performed by `DbContext` on the entity level (entities are cached within corresponding `DbSet`)
+* Second level caching is on the level of `DataReader` and holds raw query data (however, there is no out-of-the-box 2nd
+level caching mechanism in Entity Framework 6).
+
+Ignite.NET provides an EF6 second level caching solution that stores data in a distributed Ignite cache. This is ideal
+for scenarios with multiple application servers using a single SQL database via Entity Framework - cached queries are
+shared between all machines in the cluster.
+
+== Installation
+* *Binary distribution*: add a reference to `Apache.Ignite.EntityFramework.dll`
+* *NuGet*: `Install-Package Apache.Ignite.EntityFramework`
+
+== Configuration
+
+Ignite.NET provides a custom `DbConfiguration` implementation which enables second level caching - `Apache.Ignite.EntityFramework.IgniteDbConfiguration`.
+There is a number of ways to apply `DbConfiguration` to the EntityFramework `DbContext`. See the following MSDN document
+for details: https://msdn.microsoft.com/en-us/library/jj680699[msdn.microsoft.com/en-us/library/jj680699, window=_blank].
+
+The simplest way to implement this is to use the `[DbConfigurationType]` attribute:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+[DbConfigurationType(typeof(IgniteDbConfiguration))]
+class MyContext : DbContext
+{
+  public virtual DbSet<Foo> Foos { get; set; }
+  public virtual DbSet<Bar> Bars { get; set; }
+}
+----
+--
+
+To customize caching behavior, create a class that inherits `IgniteDbConfiguration` and call one of the base constructors.
+The example below shows the most custom base constructor:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+private class MyDbConfiguration : IgniteDbConfiguration
+{
+  public MyDbConfiguration()
+    : base(
+      // IIgnite instance to use
+      Ignition.Start(),
+      // Metadata cache configuration (small cache, does not tolerate data loss)
+      // Should be replicated or partitioned with backups
+      new CacheConfiguration("metaCache")
+      {
+        CacheMode = CacheMode.Replicated
+      },
+      // Data cache configuration (large cache, holds actual query results,
+      // tolerates data loss). Can have no backups.
+      new CacheConfiguration("dataCache")
+      {
+        CacheMode = CacheMode.Partitioned,
+        Backups = 0
+      },
+      // Custom caching policy.
+      new MyCachingPolicy())
+    {
+      // No-op.
+    }
+}
+
+// Apply custom configuration to the DbContext
+[DbConfigurationType(typeof(MyDbConfiguration))]
+class MyContext : DbContext
+{
+  ...
+}
+----
+--
+
+=== Caching Policy
+
+The caching policy feature controls a selected caching mode, expiration, and which entity sets should be cached. With the default
+`null` policy, all entity sets are cached in the `ReadWrite` mode with no expiration. A caching policy can be configured
+by implementing the `IDbCachingPolicy` interface or inheriting the `DbCachingPolicy` class. The example below shows a sample implementation:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+public class DbCachingPolicy : IDbCachingPolicy
+{
+  /// <summary>
+  /// Determines whether the specified query can be cached.
+  /// </summary>
+  public virtual bool CanBeCached(DbQueryInfo queryInfo)
+  {
+    // This method is called before database call.
+    // Cache only Persons.
+    return queryInfo.AffectedEntitySets.All(x => x.Name == "Person");
+  }
+
+  /// <summary>
+  /// Determines whether specified number of rows should be cached.
+  /// </summary>
+  public virtual bool CanBeCached(DbQueryInfo queryInfo, int rowCount)
+  {
+    // This method is called after database call.
+    // Cache only queries that return less than 1000 rows.
+    return rowCount < 1000;
+  }
+
+  /// <summary>
+  /// Gets the absolute expiration timeout for a given query.
+  /// </summary>
+  public virtual TimeSpan GetExpirationTimeout(DbQueryInfo queryInfo)
+  {
+    // Cache for 5 minutes.
+    return TimeSpan.FromMinutes(5);
+  }
+
+  /// <summary>
+  /// Gets the caching strategy for a given query.
+  /// </summary>
+  public virtual DbCachingMode GetCachingMode(DbQueryInfo queryInfo)
+  {
+    // Cache with invalidation.
+    return DbCachingMode.ReadWrite;
+  }
+}
+----
+--
+
+=== Caching Modes
+
+[cols="1,3",opts="header"]
+|===
+|DbCachingMode |Description
+|`ReadOnly`| Read-only mode, never invalidates. Database updates are ignored in this mode. Once query results have been
+cached, they are kept in cache until expired (forever when no expiration is specified). This mode is suitable for data
+that is not expected to change (like a list of countries and other dictionary data).
+|`ReadWrite`| Read-write mode. Cached data is invalidated when underlying entity set changes. This is "normal" cache mode
+which always provides correct query results. Keep in mind that this mode works correctly only when all database changes
+are performed via DbContext with Ignite caching configured. Other database updates are not tracked.
+|===
+
+== app.config & web.config
+
+Ignite caching can be enabled in the config files by providing an assembly-qualified type name of `IgniteDbConfiguration` (or your class that inherits it):
+
+[tabs]
+--
+tab:app.config[]
+[source,xml]
+----
+<entityFramework codeConfigurationType="Apache.Ignite.EntityFramework.IgniteDbConfiguration, Apache.Ignite.EntityFramework">
+    ...Your EF config...
+</entityFramework>
+----
+--
+
+== Advanced Configuration
+
+When there is no possibility to inherit `IgniteDbConfiguration` (it already inherits some other class), you can call the
+`IgniteDbConfiguration.InitializeIgniteCaching` static method from the constructor, passing `this` as the first argument:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+private class MyDbConfiguration : OtherDbConfiguration
+{
+  public MyDbConfiguration() : base(...)
+  {
+    IgniteDbConfiguration.InitializeIgniteCaching(this, Ignition.GetIgnite(), null, null, null);
+  }
+}
+----
+--
diff --git a/docs/_docs/net-specific/java-services-execution.adoc b/docs/_docs/net-specific/java-services-execution.adoc
new file mode 100644
index 0000000..e0a8827
--- /dev/null
+++ b/docs/_docs/net-specific/java-services-execution.adoc
@@ -0,0 +1,102 @@
+= Java Services Execution from Ignite.NT
+
+== Overview
+
+Ignite.NET can work with Java services the same way as with .NET services. To call a Java service from a .NET application,
+you need to know the interface of the service.
+
+== Example
+
+Let's review how to use this capability by taking a usage example.
+
+=== Create Java Service
+
+[tabs]
+--
+tab:Java[]
+[source,java]
+----
+public class MyJavaService implements Service {
+  // Service method to be called from .NET
+  public String testToUpper(String x) {
+    return x.toUpperCase();
+  }
+
+  // Service interface implementation
+  @Override public void cancel(ServiceContext context) { // No-op.  }
+  @Override public void init(ServiceContext context) throws Exception { // No-op. }
+  @Override public void execute(ServiceContext context) throws Exception { // No-op. }
+}
+----
+--
+
+This Java service can be deployed on any nodes (.NET, C{pp}, Java-only), so there are no restrictions on deployment options:
+
+[tabs]
+--
+tab:Java[]
+[source,java]
+----
+ignite.services().deployClusterSingleton("myJavaSvc", new MyJavaService());
+----
+--
+
+=== Call Java Service From .NET
+
+Create a version of the service interface for .NET:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+// Interface can have any name
+interface IJavaService
+{
+  // Method must have the same name (case-sensitive) and same signature:
+  // argument types and order.
+  // Argument names and return type do not matter.
+  string testToUpper(string str);
+}
+----
+--
+
+Get the service proxy and invoke the method:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var config = new IgniteConfiguration
+{
+  // Make sure that Java service class is in classpath on all nodes, including .NET
+  JvmClasspath = @"c:\my-project\src\Java\target\classes\"
+}
+
+var ignite = Ignition.Start(config);
+
+// Make sure to use the same service name as in deployment
+var prx = ignite.GetServices().GetServiceProxy<IJavaService>("myJavaSvc");
+string result = prx.testToUpper("invoking Java service...");
+Console.WriteLine(result);
+----
+--
+
+== Interface Methods Mapping
+
+The .NET service interface is mapped to its Java counterpart dynamically. This happens at the time of the method invocation:
+
+* It is not necessary to specify all Java service methods in the .NET interface.
+* The .NET interface can have members that are not present in Java service. You won't get any exception until you call these missing methods.
+
+The Java methods are resolved the following way:
+
+* Ignite looks for a method with the specified name and parameter count. If there is only one exist, Ignite will use it.
+* Among the matched methods Ignite looks for a method with compatible arguments (via `Class.isAssignableFrom`).
+Ignite invoke the matched method or throws an exception in case of ambiguity.
+* The method return type is ignored, since .NET and Java do not allow identical methods with different return types.
+
+See link:net-specific/platform-interoperability[Platform Interoperability, Type Compatibility section] for details on
+method arguments and result mapping. Note, that the `params/varargs` are also supported, since in .NET and Java these are
+syntactic sugar for object arrays.
diff --git a/docs/_docs/net-specific/linq.adoc b/docs/_docs/net-specific/linq.adoc
new file mode 100644
index 0000000..215fc95
--- /dev/null
+++ b/docs/_docs/net-specific/linq.adoc
@@ -0,0 +1,242 @@
+= Apache Ignite.NET LINQ Provider
+
+== Overview
+
+Apache Ignite.NET includes a LINQ provider that is integrated with Ignite SQL APIs. You can avoid dealing with SQL
+syntax directly and write queries in C# with LINQ. The Ignite LINQ provider supports all features of ANSI-99 SQL including
+distributed joins, groupings, aggregates, sorting, and more.
+
+== Installation
+
+* If you use the Ignite *binary distribution*: add a reference to `Apache.Ignite.Linq.dll`
+* If you use *NuGet*: `Install-Package Apache.Ignite.Linq`
+
+== Configuration
+
+SQL indexes need to be configured the same way as for regular SQL queries, see link:SQL/indexes[Defining Indexes section]
+for details.
+
+== Usage
+
+`Apache.Ignite.Linq.CacheLinqExtensions` class is an entry point for the LINQ provider.
+Obtain a queryable instance over an Ignite cache by calling the `AsCacheQueryable` method, and use LINQ on it:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+ICache<EmployeeKey, Employee> employeeCache = ignite.GetCache<EmployeeKey, Employee>(CacheName);
+
+IQueryable<ICacheEntry<EmployeeKey, Employee>> queryable = cache.AsCacheQueryable();
+
+Employee[] interns = queryable.Where(emp => emp.Value.IsIntern).ToArray();
+----
+--
+
+[CAUTION]
+====
+[discrete]
+You can use LINQ directly on the cache instance, without calling `AsCacheQueryable()`. However, this will result in LINQ
+to Objects query that fetches and processes entire cache data set locally, which is very inefficient.
+====
+
+== Introspection
+
+The Ignite LINQ provider uses `ICache.QueryFields` underneath. You can examine produced `SqlFieldsQuery` by casting
+`IQueryable` to `ICacheQueryable` at any point before materializing statements (`ToList`, `ToArray`, etc):
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+// Create query
+var query = ignite.GetCache<EmployeeKey, Employee>(CacheName).AsCacheQueryable().Where(emp => emp.Value.IsIntern);
+
+// Cast to ICacheQueryable
+var cacheQueryable = (ICacheQueryable) query;
+
+// Get resulting fields query
+SqlFieldsQuery fieldsQuery = cacheQueryable.GetFieldsQuery();
+
+// Examine generated SQL
+Console.WriteLine(fieldsQuery.Sql);
+
+// Output: select _T0._key, _T0._val from "persons".Person as _T0 where _T0.IsIntern
+----
+--
+
+== Projections
+
+Simple `Where` queries operate on `ICacheEntry` objects. You can select Key, Value, or any of the Key and Value fields
+separately. Multiple fields can be selected using anonymous types.
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var query = ignite.GetCache<EmployeeKey, Employee>(CacheName).AsCacheQueryable().Where(emp => emp.Value.IsIntern);
+
+IQueryable<EmployeeKey> keys = query.Select(emp => emp.Key);
+
+IQueryable<Employee> values = query.Select(emp => emp.Value);
+
+IQueryable<string> names = values.Select(emp => emp.Name);
+
+var custom = query.Select(emp => new {Id = emp.Key, Name = emp.Value.Name, Age = emp.Value.Age});
+----
+--
+
+== Compiled Queries
+
+The LINQ provider causes certain overhead caused by expression parsing and SQL generation. You may want to eliminate this
+overhead for frequently used queries.
+
+The `Apache.Ignite.Linq.CompiledQuery` class supports queries compilation. Call the `Compile` method to create a new delegate
+to represent the compiled query. All query parameters should be in the delegate parameters.
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var queryable = ignite.GetCache<EmployeeKey, Employee>(CacheName).AsCacheQueryable();
+
+// Regular query
+var persons = queryable.Where(emp => emp.Value.Age > 21);
+var result = persons.ToArray();
+
+// Corresponding compiled query
+var compiledQuery = CompiledQuery.Compile((int age) => queryable.Where(emp => emp.Value.Age > age));
+IQueryCursor<ICacheEntry<EmployeeKey, Employee>> cursor = compiledQuery(21);
+result = cursor.ToArray();
+----
+--
+
+Refer to the https://ptupitsyn.github.io/LINQ-vs-SQL-in-Ignite/[LINQ vs SQL blog post, window=_blank] for more details
+on the LINQ provider performance.
+
+== Joins
+
+The LINQ provider support JOINs that span several caches/tables and nodes.
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var persons = ignite.GetCache<int, Person>("personCache").AsCacheQueryable();
+var orgs = ignite.GetCache<int, Organization>("orgCache").AsCacheQueryable();
+
+// SQL join on Person and Organization to find persons working for Apache
+var qry = from person in persons from org in orgs
+          where person.Value.OrgId == org.Value.Id
+          && org.Value.Name == "Apache"
+          select person
+
+foreach (var cacheEntry in qry)
+    Console.WriteLine(cacheEntry.Value);
+
+// Same query with method syntax
+qry = persons.Join(orgs, person => person.Value.OrgId, org => org.Value.Id,
+(person, org) => new {person, org}).Where(p => p.org.Name == "Apache").Select(p => p.person);
+----
+--
+
+== Contains
+
+`ICollection.Contains` is supported, which is useful when we want to retrieve data by a set of ids, for example:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var persons = ignite.GetCache<int, Person>("personCache").AsCacheQueryable();
+var ids = new int[] {1, 20, 56};
+
+var personsByIds = persons.Where(p => ids.Contains(p.Value.Id));
+----
+--
+
+This query translates into the `... where Id IN (?, ?, ?)` command. However, keep in mind, that this form cannot be used
+in compiled queries because of variable argument number. Better alternative is to use `Join` on the `ids` collection:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var persons = ignite.GetCache<int, Person>("personCache").AsCacheQueryable();
+var ids = new int[] {1, 20, 56};
+
+var personsByIds = persons.Join(ids,
+                                person => person.Value.Id,
+                                id => id,
+                                (person, id) => person);
+----
+--
+
+This LINQ query translates to a temp table join:
+`select _T0._KEY, _T0._VAL from "person".Person as _T0 inner join table (F0 int = ?) _T1 on (_T1.F0 = _T0.ID)`,
+and has a single array parameter, so the plan can be cached properly, and compiled queries are also allowed.
+
+== Supported SQL Functions
+
+Below is a list of .NET functions and their SQL equivalents that are supported by the Ignite LINQ provider.
+
+[width="100%",cols="1,3",opts="header"]
+|===
+|`String.Length`| `LENGTH`
+|`String.ToLower`| `LOWER`
+|`String.ToUpper`| `UPPER`
+|`String.StartsWith("foo")`| `LIKE 'foo%'`
+|`String.EndsWith("foo")`| `LIKE '%foo'`
+|`String.Contains("foo")`| `LIKE '%foo%'`
+|`String.IndexOf("abc")`| `INSTR(MyField, 'abc') - 1`
+|`String.IndexOf("abc", 3)`| `INSTR(MyField, 'abc', 3) - 1`
+|`String.Substring("abc", 4)`| `SUBSTRING(MyField, 4 + 1)`
+|`String.Substring("abc", 4, 7)`| `SUBSTRING(MyField, 4 + 1, 7)`
+|`String.Trim()`| `TRIM`
+|`String.TrimStart()`| `LTRIM`
+|`String.TrimEnd()`| `RTRIM`
+|`String.Trim('x')`| `TRIM(MyField, 'x')`
+|`String.TrimStart('x')`| `LTRIM(MyField, 'x')`
+|`String.TrimEnd('x')`| `RTRIM(MyField, 'x')`
+|`String.Replace`| `REPLACE`
+|`String.PadLeft`| `LPAD`
+|`String.PadRight`| `RPAD`
+|`Regex.Replace`| `REGEXP_REPLACE`
+|`Regex.IsMatch`| `REGEXP_LIKE`
+|`Math.Abs`| `ABS`
+|`Math.Acos`| `ACOS`
+|`Math.Asin`| `ASIN`
+|`Math.Atan`| `ATAN`
+|`Math.Atan2`| `ATAN2`
+|`Math.Ceiling`| `CEILING`
+|`Math.Cos`| `COS`
+|`Math.Cosh`| `COSH`
+|`Math.Exp`| `EXP`
+|`Math.Floor`| `FLOOR`
+|`Math.Log`| `LOG`
+|`Math.Log10`| `LOG10`
+|`Math.Pow`| `POWER`
+|`Math.Round`| `ROUND`
+|`Math.Sign`| `SIGN`
+|`Math.Sin`| `SIN`
+|`Math.Sinh`| `SINH`
+|`Math.Sqrt`| `SQRT`
+|`Math.Tan`| `TAN`
+|`Math.Tanh`| `TANH`
+|`Math.Truncate`| `TRUNCATE`
+|`DateTime.Year`| `YEAR`
+|`DateTime.Month`| `MONTH`
+|`DateTime.Day`| `DAY_OF_MONTH`
+|`DateTime.DayOfYear`| `DAY_OF_YEAR`
+|`DateTime.DayOfWeek`| `DAY_OF_WEEK - 1`
+|`DateTime.Hour`| `HOUR`
+|`DateTime.Minute`| `MINUTE`
+|`DateTime.Second`| `SECOND`
+|===
diff --git a/docs/_docs/net-specific/net-logging.adoc b/docs/_docs/net-specific/net-logging.adoc
new file mode 100644
index 0000000..7678fe1
--- /dev/null
+++ b/docs/_docs/net-specific/net-logging.adoc
@@ -0,0 +1,119 @@
+= Ignite.NET Logging
+
+== Overview
+By default, Ignite uses underlying the Java log4j logging system. Log messages from both .NET and Java are recorded there.
+You can also write to this log via an `IIgnite.Logger` instance:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var ignite = Ignition.Start();
+ignite.Logger.Info("Hello World!");
+----
+--
+
+`LoggerExtensions` class provides convenient shortcuts for `ILogger.Log` method.
+
+== Custom Logger
+
+You can provide a logger implementation via the `IgniteConfiguration.Logger` and `ILogger` interface.
+Messages from both .NET and Java will be redirected there.
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var cfg = new IgniteConfiguration
+{
+  Logger = new MemoryLogger()
+}
+
+var ignite = Ignition.Start();
+
+class MemoryLogger : ILogger
+{
+  // Logger can be called from multiple threads, use concurrent collection
+  private readonly ConcurrentBag<string> _messages = new ConcurrentBag<string>();
+
+  public void Log(LogLevel level, string message, object[] args,
+                  IFormatProvider formatProvider, string category,
+                  string nativeErrorInfo, Exception ex)
+  {
+    _messages.Add(message);
+  }
+
+  public bool IsEnabled(LogLevel level)
+  {
+    // Accept any level.
+    return true;
+  }
+}
+----
+tab:app.config[]
+[source,xml]
+----
+<igniteConfiguration>
+<logger type="MyNamespace.MemoryLogger, MyAssembly" />
+</igniteConfiguration>
+----
+--
+
+== NLog & log4net Loggers
+
+Ignite.NET provides `ILogger` implementations for http://nlog-project.org/[NLog, window=_blank] and https://logging.apache.org/log4net/[Apache log4net, window=_blank].
+They are included in the binary package (`Apache.Ignite.NLog.dll` and `Apache.Ignite.Log4Net.dll`) and can be installed via NuGet:
+
+* `Install-Package Apache.Ignite.NLog`
+* `Install-Package Apache.Ignite.Log4Net`
+
+NLog and Log4Net use statically defined configuration, so there is nothing to configure in Ignite besides `IgniteConfiguration.Logger`:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var cfg = new IgniteConfiguration
+{
+  Logger = new IgniteNLogLogger()  // or IgniteLog4NetLogger
+}
+
+var ignite = Ignition.Start();
+----
+tab:app.config[]
+[source,xml]
+----
+<igniteConfiguration>
+  <logger type="Apache.Ignite.NLog.IgniteNLogLogger, Apache.Ignite.NLog" />
+</igniteConfiguration>
+----
+--
+
+A simple file-based logging with NLog can be set up like this:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var nlogConfig = new LoggingConfiguration();
+
+var fileTarget = new FileTarget
+{
+  FileName = "ignite_nlog.log"
+};
+nlogConfig.AddTarget("logfile", fileTarget);
+
+nlogConfig.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, fileTarget));
+LogManager.Configuration = nlogConfig;
+
+var igniteConfig = new IgniteConfiguration
+{
+  Logger = new IgniteNLogLogger()
+};
+Ignition.Start(igniteConfig);
+----
+--
diff --git a/docs/_docs/net-specific/net-plugins.adoc b/docs/_docs/net-specific/net-plugins.adoc
new file mode 100644
index 0000000..d753a9f
--- /dev/null
+++ b/docs/_docs/net-specific/net-plugins.adoc
@@ -0,0 +1,155 @@
+= Extending Ignite.NET With Custom Plugins
+
+== Overview
+
+The Ignite.NET plugin system allows you to extend the core Ignite.NET functionality with custom plugins. The best way to
+explain how Ignite plugins work is by looking at the life cycle of plugins.
+
+== IgniteConfiguration.PluginConfigurations
+
+First, an Apache Ignite plugin has to be registered via the `IgniteConfiguration.PluginConfigurations` property which is
+a collection of the `IPluginConfiguration` implementations. From a user's perspective, this is a manual process - a
+plugin's assembly has to be referenced and configured explicitly.
+
+The `IPluginConfiguration` interface has two members that interact with the Java part of Apache Ignite.NET. This is
+described in the next section. Besides those two members, `IPluginConfiguration` implementation should contain all the
+other plugin-specific configuration properties.
+
+Another part of an `IPluginConfiguration` implementation is the mandatory `[PluginProviderType]` attribute that tethers a
+plugin configuration with a plugin implementation. For example:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+[PluginProviderType(typeof(MyPluginProvider))]
+    public class MyPluginConfiguration : IPluginConfiguration
+    {
+        public string MyProperty { get; set; }  // Plugin-specific property
+
+        public int? PluginConfigurationClosureFactoryId
+        {
+            get { return null; }  // No Java part
+        }
+
+        public void WriteBinary(IBinaryRawWriter writer)
+        {
+            // No-op.
+        }
+    }
+----
+--
+
+To recap, this is how plugins are added and initialized:
+
+* You add the `IPluginConfiguration` implementation instance to `IgniteConfiguration`.
+* You start an Ignite node with the prepared configuration.
+* Before the Ignite node initialization is finished, the Ignite plugin engine examines the `IPluginConfiguration` implementation
+for the `[PluginProviderType]` attribute and instantiates the specified class.
+
+== IPluginProvider
+
+The `IPluginProvider` implementation is the work-horse of the newly added plugin. It deals with the Ignite node life cycle
+by processing the calls to the `OnIgniteStart` and `OnIgniteStop` methods. In addition, it can provide an optional API
+to be used by an end user via the `GetPlugin<T>()` method.
+
+The first method to be invoked on the `IPluginProvider` implementation by the Ignite.NET engine is
+`Start(IPluginContext<TestIgnitePluginConfiguration> context)`. `IPluginContext` provides an access to an initial plugin
+configuration and all means to interact with Ignite.
+
+When Ignite is being stopped, the `Stop` and `OnIgniteStop` methods are executed sequentially so that the plugin
+implementation can accomplish all cleanup and shutdown-related​ tasks.
+
+== IIgnite.GetPlugin
+
+Plugins can expose user-facing API which is accessed via the `IIgnite.GetPlugin(string name)` method. The Ignite engine
+will search for `IPluginProvider` with the passed name and call `GetPlugin` on it.
+
+== Interacting With Java
+
+The Ignite.NET plugin can interact with an Ignite Java plugin via the `PlatformTarget` & `IPlatformTarget` interface pair.
+
+=== Java-Specific Logic
+
+. Implement the `PlatformTarget` interface, which is a communication point with .NET:
++
+[tabs]
+--
+tab:Java[]
+[source,java]
+----
+class MyPluginTarget implements PlatformTarget {
+  @Override public long processInLongOutLong(int type, long val) throws IgniteCheckedException {
+    if (type == 1)
+        return val + 1;
+    else
+      return val - 1;
+  }
+  ...  // Other methods here.
+}
+----
+--
+
+* Implement the `PlatformPluginExtension` interface:
++
+[tabs]
+--
+tab:Java[]
+[source,java]
+----
+public class MyPluginExtension implements PlatformPluginExtension {
+  @Override public int id() {
+    return 42;  // Unique id to be used from .NET side.
+  }
+
+  @Override public PlatformTarget createTarget() {
+    return new MyPluginTarget();  // Return target from previous step.
+  }
+}
+----
+--
+
+* Implement the `PluginProvider.initExtensions` method and register the `PlatformPluginExtension` class:
++
+[tabs]
+--
+tab:Java[]
+[source,java]
+----
+@Override public void initExtensions(PluginContext ctx, ExtensionRegistry registry) {
+  registry.registerExtension(PlatformPluginExtension.class, new MyPluginExtension());
+}
+----
+--
+
+=== .NET-specific Logic
+
+Call `IPluginContext.GetExtension` with a corresponding id. This will invoke the `createTarget` call on the Java side:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+IPlatformTarget extension = pluginContext.GetExtension(42);
+
+long result = extension.InLongOutLong(1, 2);  // processInLongOutLong is called in Java
+----
+--
+
+Other `IPlatformTarget` methods provide an efficient way to exchange any kind of data between Java and .NET code.
+
+=== Callbacks from Java
+
+.NET \-> Java call mechanism is described above; you can also do Java \-> .NET calls:
+
+* Register callback handler with some ID on the .NET side via the `IPluginContext.RegisterCallback` method.
+* Call `PlatformCallbackGateway.pluginCallback` with that ID on the Java side.
+
+[NOTE]
+====
+[discrete]
+=== Complete Example
+A detailed walk-through plugin example can be found in https://ptupitsyn.github.io/Ignite-Plugin/[this blog post, window=_blank].
+====
diff --git a/docs/_docs/net-specific/net-serialization.adoc b/docs/_docs/net-specific/net-serialization.adoc
new file mode 100644
index 0000000..8176e69
--- /dev/null
+++ b/docs/_docs/net-specific/net-serialization.adoc
@@ -0,0 +1,300 @@
+= Serialization in Ignite.NET
+
+Most of the user-defined classes going through the Ignite .NET API will be trasferred over the network to other cluster nodes. These classes include:
+
+* Cache keys and values
+* Cache processors and filters (`ICacheEntryProcessor`, `ICacheEntryFilter`, `ICacheEntryEventFilter`, `ICacheEntryEventListener`)
+* Compute functions (`IComputeFunc`), actions (`IComputeAction`) and jobs (`IComputeJob`)
+* Services (`IService`)
+* Event and Message handlers (`IEventListener`, `IEventFilter`, `IMessageListener`)
+
+Passing objects of these classes over the network requires serialization. Ignite .NET supports the following ways of serializing user data:
+
+* `Apache.Ignite.Core.Binary.IBinarizable` interface
+* `Apache.Ignite.Core.Binary.IBinarySerializer` interface
+* `System.Runtime.Serialization.ISerializable` interface
+* Ignite reflective serialization (when none of the above applies)
+
+== IBinarizable
+
+`IBinarizable` approach provides a fine-grained control over serialization. This is a preferred way for high-performance production code.
+
+First, implement the `IBinarizable` interface in your class:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+public class Address : IBinarizable
+{
+    public string Street { get; set; }
+
+    public int Zip { get; set; }
+
+    public void WriteBinary(IBinaryWriter writer)
+    {
+        // Alphabetic field order is required for SQL DML to work.
+        // Even if DML is not used, alphabetic order is recommended.
+        writer.WriteString("street", Street);
+        writer.WriteInt("zip", Zip);
+    }
+
+    public void ReadBinary(IBinaryReader reader)
+    {
+        // Read order does not matter, however, reading in the same order
+        // as writing improves performance.
+        Street = reader.ReadString("street");
+        Zip = reader.ReadInt("zip");
+    }
+}
+----
+--
+
+`IBinarizable` can also be implemented in raw mode, without field names. This provides the fastest and the most compact
+serialization, but disables SQL queries:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+public class Address : IBinarizable
+{
+    public string Street { get; set; }
+
+    public int Zip { get; set; }
+
+    public void WriteBinary(IBinaryWriter writer)
+    {
+        var rawWriter = writer.GetRawWriter();
+
+        rawWriter.WriteString(Street);
+        rawWriter.WriteInt(Zip);
+    }
+
+    public void ReadBinary(IBinaryReader reader)
+    {
+        // Read order must be the same as write order
+        var rawReader = reader.GetRawReader();
+
+        Street = rawReader.ReadString();
+        Zip = rawReader.ReadInt();
+    }
+}
+----
+--
+
+[NOTE]
+====
+[discrete]
+=== Automatic GetHashCode and Equals Implementation
+If an object can be serialized into a binary form, then Ignite will calculate its hash code during serialization and
+write it to the resulting binary array. Also, Ignite provides a custom implementation of the equals method for the
+binary object's comparison needs. This means that you do not need to override the `GetHashCode` and `Equals` methods of
+your custom keys and values in order for them to be used in Ignite.
+====
+
+== IBinarySerializer
+
+`IBinarySerializer` is similar to `IBinarizable`, but separates serialization logic from the class implementation.
+This may be useful when the class code can not be modified, and serialization logic is shared between multiple classes,
+etc. The following code has exactly the same serialization as in the `Address` example above:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+public class Address : IBinarizable
+{
+    public string Street { get; set; }
+
+    public int Zip { get; set; }
+}
+
+public class AddressSerializer : IBinarySerializer
+{
+    public void WriteBinary(object obj, IBinaryWriter writer)
+    {
+        var addr = (Address) obj;
+
+        writer.WriteString("street", addr.Street);
+        writer.WriteInt("zip", addr.Zip);
+    }
+
+    public void ReadBinary(object obj, IBinaryReader reader)
+    {
+        var addr = (Address) obj;
+
+        addr.Street = reader.ReadString("street");
+        addr.Zip = reader.ReadInt("zip");
+    }
+}
+----
+--
+
+The `Serializer` should be specified in the configuration like this:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var cfg = new IgniteConfiguration
+{
+    BinaryConfiguration = new BinaryConfiguration
+    {
+        TypeConfigurations = new[]
+        {
+            new BinaryTypeConfiguration(typeof (Address))
+            {
+                Serializer = new AddressSerializer()
+            }
+        }
+    }
+};
+
+using (var ignite = Ignition.Start(cfg))
+{
+  ...
+}
+----
+--
+
+== ISerializable
+
+Types that implement the `System.Runtime.Serialization.ISerializable` interface will be serialized accordingly
+(by calling `GetObjectData` and serialization constructor). All system features are supported: `IObjectReference`,
+`IDeserializationCallback`, `OnSerializingAttribute`, `OnSerializedAttribute`, `OnDeserializingAttribute`, `OnDeserializedAttribute`.
+
+The `GetObjectData` result is written into the Ignite binary format. The following three classes provide identical serialized representation:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+class Reflective
+{
+    public int Id { get; set; }
+    public string Name { get; set; }
+}
+
+class Binarizable : IBinarizable
+{
+    public int Id { get; set; }
+    public string Name { get; set; }
+
+    public void WriteBinary(IBinaryWriter writer)
+    {
+        writer.WriteInt("Id", Id);
+        writer.WriteString("Name", Name);
+    }
+
+    public void ReadBinary(IBinaryReader reader)
+    {
+        Id = reader.ReadInt("Id");
+        Name = reader.ReadString("Name");
+    }
+}
+
+class Serializable : ISerializable
+{
+    public int Id { get; set; }
+    public string Name { get; set; }
+
+    public Serializable() {}
+
+    protected Serializable(SerializationInfo info, StreamingContext context)
+    {
+        Id = info.GetInt32("Id");
+        Name = info.GetString("Name");
+    }
+
+    public void GetObjectData(SerializationInfo info, StreamingContext context)
+    {
+        info.AddValue("Id", Id);
+        info.AddValue("Name", Name);
+    }
+}
+----
+--
+
+== Ignite Reflective Serialization
+
+Ignite reflective serialization is essentially the `IBinarizable` approach where the interface is implemented automatically
+by reflecting over all fields and emitting write/read calls.
+
+There are no requirements for this mechanism, any class or struct can be serialized including all system types, delegates,
+expression trees, or anonymous types.
+
+Use the `[NonSerialized]` attribute to filter out specific fields during serialization.
+
+The raw mode can be enabled by specifying `BinaryReflectiveSerializer` explicitly:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var binaryConfiguration = new BinaryConfiguration
+{
+    TypeConfigurations = new[]
+    {
+        new BinaryTypeConfiguration(typeof(MyClass))
+        {
+            Serializer = new BinaryReflectiveSerializer {RawMode = true}
+        }
+    }
+};
+----
+tab:app.config[]
+[source,xml]
+----
+<igniteConfiguration>
+    <binaryConfiguration>
+        <typeConfigurations>
+            <binaryTypeConfiguration typeName='Apache.Ignite.ExamplesDll.Binary.Address'>
+                <serializer type='Apache.Ignite.Core.Binary.BinaryReflectiveSerializer, Apache.Ignite.Core' rawMode='true' />
+            </binaryTypeConfiguration>
+        </typeConfigurations>
+    </binaryConfiguration>
+</igniteConfiguration>
+----
+--
+
+Otherwise, `BinaryConfiguration` is not required.
+
+Performance is identical to manual the `IBinarizable` approach. Reflection is only used on startup to iterate over the
+fields and emit efficient IL code.
+
+Types marked with `[Serializable]` attribute but without `ISerializable` interface are written with Ignite reflective serializer.
+
+== Using Entity Framework POCOs
+
+The Entity Framework POCOs can be used directly with Ignite.
+
+However, https://msdn.microsoft.com/en-us/data/jj592886.aspx[POCO proxies, window=_blank] cannot be directly serialized
+or deserialized by Ignite, because the proxy type is a dynamic type.
+
+Make sure to disable proxy creation when using EF objects with Ignite:
+
+[tabs]
+--
+tab:Entity Framework 6[]
+[source,csharp]
+----
+ctx.Configuration.ProxyCreationEnabled = false;
+----
+tab:Entity Framework 5[]
+[source,csharp]
+----
+ctx.ContextOptions.ProxyCreationEnabled = false;
+----
+--
+
+== More Info
+
+See https://ptupitsyn.github.io/Ignite-Serialization-Performance/[Ignite Serialization Performance, window=_blank] blog
+post for more details on serialization performance of various modes introduced on this page.
diff --git a/docs/_docs/net-specific/net-troubleshooting.adoc b/docs/_docs/net-specific/net-troubleshooting.adoc
new file mode 100644
index 0000000..0d48400
--- /dev/null
+++ b/docs/_docs/net-specific/net-troubleshooting.adoc
@@ -0,0 +1,124 @@
+= Ignite.NET Troubleshooting
+
+== Overview
+
+This page covers several troubleshooting techniques and commonly-known issues you can come across while building and
+using your Ignite.NET applications in production.
+
+== Troubleshooting With Console
+
+Ignite produces console output (stdout): information, metrics, warnings, error details. If your app does not open console, you may redirect the console output to a string or a file:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+var sw = new StringWriter();
+Console.SetOut(sw);
+
+// Examine output:
+sw.ToString();
+----
+--
+
+== Getting More Insights On Exceptions
+
+When you are getting an `IgniteException`, always make sure to examine the `InnerException` property that often contains
+more details on the root cause of the issue. You can do that in Visual Studio debugger or by calling `ToString()` on the exception object:
+
+image::images/net-view-details.png[Visual Studio Debugger]
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+try {
+    IQueryCursor<List> cursor = cache.QueryFields(query);
+}
+catch (IgniteException e) {
+    // Printing out the whole exception meesage.
+    Console.WriteLine(e.ToString());
+}
+----
+--
+
+== Commonly-Known Issues
+
+The following section covers several issues you can come across while designing your Ignite.NET applications.
+
+=== Failed to load jvm.dll
+
+Make sure that Java Development Kit is installed, and the `JAVA_HOME` variable is set and points to a JDK installation directory.
+
+The `errorCode=193` code is `ERROR_BAD_EXE_FORMAT`, which is often caused by x64/x86 mismatch. Make sure that the installed
+JDK and your application have the same x64/x86 platform target. Ignite detects proper JDK automatically when `JAVA_HOME` is not set,
+so if you have x86 AND x64 JDK installed, it will work in any mode.
+
+The `126 ERROR_MOD_NOT_FOUND` code can occur due to missing dependencies:
+
+* JDK 8 requires https://www.microsoft.com/en-us/download/details.aspx?id=14632[Microsoft Visual C{pp} 2010 Redistributable Package, window=_blank]
+* Later JDK versions require https://www.microsoft.com/en-us/download/details.aspx?id=48145[Microsoft Visual C{pp} 2015 Redistributable Package, window=_blank] or later
+
+=== Java class is not found
+
+Check your the `IGNITE_HOME` environment variable, `IgniteConfiguration.IgniteHome` and `IgniteConfiguration.JvmClasspath` properties.
+Refer to link:net-specific/deployment-options[Deployment] section for more details. ASP.NET/IIS scenarios require additional steps.
+
+=== Freeze on Ignition.Start
+
+Examine console output. Most often this is caused by a topology join failure:
+
+* Ignite `DiscoverySpi` settings are incorrect
+* `ClientMode` is true, but there are no servers nodes that form the cluster.
+
+=== Failed to start manager : GridManagerAdapter
+
+Examine console output. Most often this is caused by an invalid or incompatible configuration:
+
+* Some configuration property has an invalid value (out of range and the like).
+* Some configuration property is incompatible with a value in other cluster nodes. In particular, `BinaryConfiguration` properties,
+such as `CompactFooter`, `IdMapper`, and `NameMapper` should be the same on all nodes.
+
+The latter problem often arises when building a mixed cluster (Java + .NET nodes), because default configuration on these
+platforms is different. .NET only supports `BinaryBasicIdMapper` and `BinaryBasicNameMapper`. Java configuration has to
+be fixed the following way to enable .NET nodes connectivity:
+
+[tabs]
+--
+tab:XML[]
+[source,xml]
+----
+<property name="binaryConfiguration">
+    <bean class="org.apache.ignite.configuration.BinaryConfiguration">
+        <property name="compactFooter" value="true"/>
+        <property name="idMapper">
+            <bean class="org.apache.ignite.binary.BinaryBasicIdMapper">
+                <constructor-arg value="true"/>
+            </bean>
+        </property>
+        <property name="nameMapper">
+            <bean class="org.apache.ignite.binary.BinaryBasicNameMapper">
+                <constructor-arg value="true"/>
+            </bean>
+        </property>
+    </bean>
+</property>
+----
+--
+
+=== Could not load file or assembly 'MyAssembly' or one of its dependencies. The system cannot find the file specified.
+
+This exception can occur due to missing assemblies on remote nodes.
+See link:net-specific/standalone-nodes#load-user-assemblies[Standalone Nodes: Loading User Assemblies] for details.
+
+=== Stack smashing detected: dotnet terminated
+
+This happens on Linux with .NET Core when `NullReferenceException` occurs in user code. The reason is that both .NET and
+Java use `SIGSEGV` to handle certain exceptions, including `NullPointerException` and `NullReferenceException`, and when
+JVM runs in the same process as .NET, it overrides that handler, breaking .NET exception handling
+(see https://github.com/dotnet/coreclr/issues/25945[1, window=_blank], https://github.com/dotnet/coreclr/issues/25166[2, window=_blank]).
+
+The fix for this issue exists in .NET Core 3.0 (https://github.com/dotnet/coreclr/pull/25972[#25972, window=_blank].
+by setting the `COMPlus_EnableAlternateStackCheck` environment variable to `1`.
diff --git a/docs/_docs/net-specific/platform-interoperability.adoc b/docs/_docs/net-specific/platform-interoperability.adoc
new file mode 100644
index 0000000..3c2b4db
--- /dev/null
+++ b/docs/_docs/net-specific/platform-interoperability.adoc
@@ -0,0 +1,181 @@
+= Platform Interoperability
+
+Ignite allows different platforms, such as .NET, Java and C{pp}, to interoperate with each other.
+Classes and objects defined and written to Ignite by one platform can be read and used by another platform.
+
+== Identifiers
+
+To achieve interoperability Ignite writes objects using the common binary format. This format encodes object type and
+fields using integer identifiers.
+
+To transform an object's type and field names to an integer value, Ignite passes them through two stage:
+
+* Name transformation: full type name and field names are passed to `IBinaryNameMapper` interface and converted to some common form.
+* ID transformation: resulting strings are passed to `IBinaryIdMapper` to produce either type ID or field ID.
+
+Mappers can be set either globally in `BinaryConfiguration` or for concrete type in `BinaryTypeConfiguration`.
+
+Java has the same interfaces `BinaryNameMapper` and `BinaryIdMapper`. They are set on `BinaryConfiguration` or `BinaryTypeConfiguration`.
+
+.NET and Java types must map to the same type ID and relevant fields must map to the same field ID.
+
+== Default Behavior
+
+The .NET part of Ignite.NET applies the following conversions by default:
+
+* Name transformation: the `System.Type.FullName` property for non-generics types; field or property name is unchanged.
+* ID transformation: names are converted to lower case and then ID is calculated in the same way as in the `java.lang.String.hashCode()` method in Java.
+
+The Java part of Ignite.NET applies the following conversions by default:
+
+* Name transformation: the `Class.getName()` method to get class name; field name is unchanged.
+* ID transformation: names are converted to lower case and then `java.lang.String.hashCode()` is used to calculate IDs.
+
+For example, the following two types will automatically map to each other, if they are outside namespaces (.NET) and packages (Java):
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+class Person
+{
+    public int Id { get; set; }
+    public string Name { get; set; }
+    public byte[] Data { get; set; }
+}
+----
+tab:Java[]
+[source,java]
+----
+class Person
+{
+    public int id;
+    public String name;
+    public byte[] data;
+}
+----
+--
+
+However, the types are normally within some namespace or package. And naming conventions for packages and namespaces
+differ in Java and .NET. It may be problematic to have .NET namespace be the same as Java package.
+
+Simple name mapper (which ignores namespace) can be used to avoid this problem. It should be configured both for .NET and Java:
+
+[tabs]
+--
+tab:Java Spring XML[]
+[source,xml]
+----
+<bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+    ...
+    <property name="binaryConfiguration">
+        <bean class="org.apache.ignite.configuration.BinaryConfiguration">
+            <property name="nameMapper">
+                <bean class="org.apache.ignite.binary.BinaryBasicNameMapper">
+                    <property name="simpleName" value="true"/>
+                </bean>
+            </property>
+        </bean>
+    </property>
+    ...
+</bean>
+----
+tab:C#[]
+[source,csharp]
+----
+var cfg = new IgniteConfiguration
+{
+  BinaryConfiguration = new BinaryConfiguration
+  {
+    NameMapper = new BinaryBasicNameMapper {IsSimpleName = true}
+  }
+}
+----
+tab:app.config[]
+[source,xml]
+----
+<igniteConfiguration>
+  <binaryConfiguration>
+    <nameMapper type="Apache.Ignite.Core.Binary.BinaryBasicNameMapper, Apache.Ignite.Core" isSimpleName="true" />
+  </binaryConfiguration>
+</igniteConfiguration>
+----
+--
+
+== Types Compatibility
+
+[width="100%",cols="1,3",opts="header"]
+|===
+|`C#`| `Java`
+|`bool`| `boolean`
+|`byte (*), sbyte`| `byte`
+|`short, ushort (*)`| `short`
+|`int, uint (*)`| `int`
+|`long, ulong (*)`| `long`
+|`char`| `char`
+|`float`| `float`
+|`double`| `double`
+|`decimal`| `java.math.BigDecimal (**)`
+|`string`| `java.lang.String`
+|`Guid`| `java.util.UUID`
+|`DateTime`| `java.util.Date, java.sql.Timestamp`
+|===
+`* byte, ushort, uint, ulong` do not have Java counterparts, and are mapped directly byte-by-byte (no range check).
+For example, `byte` value of `200` in C# will result in signed `byte` value of `-56` in Java.
+
+`** Java BigDecimal` has arbitrary size and precision, while C# decimal is fixed to 16 bytes and 28-29 digit precision. Ignite.NET will throw `BinaryObjectException` if a `BigDecimal` value does not fit into `decimal` on deserialization.
+
+`Enum` - In Ignite, Java `writeEnum` can only write ordinal values, but in .NET you can assign any number to the `enumValue`.
+So, note that any custom enum-to-primitive value bindings are not taken into account.
+
+[CAUTION]
+====
+[discrete]
+=== DateTime Serialization
+DateTime can be Local and UTC; Java Timestamp can only be UTC. Because of that, Ignite.NET can serialize DateTime in
+following ways:
+
+* .NET style (can work with non-UTC values, does not work in SQL) and as Timestamp (throws exception on non-UTC values, works properly in SQL).
+
+* Reflective serialization: mark field with `[QuerySqlField]` to enforce Timestamp serialization, or set `BinaryReflectiveSerializer.ForceTimestamp`
+to true; this can be done on per-type basis, or globally like this:
+`IgniteConfiguration.BinaryConfiguration = new BinaryConfiguration { Serializer = new BinaryReflectiveSerializer { ForceTimestamp = true } }`
+
+* `IBinarizable`: use IBinaryWriter.WriteTimestamp method.
+
+When it is not possible to modify class to mark fields with `[QuerySqlField]` or implement `IBinarizable`, use the `IBinarySerializer` approach.
+See link:net-specific/net-serialization[Serialization page] for more details.
+====
+
+== Collection Compatibility
+
+Arrays of simple types (from the table above) and arrays of objects are interoperable in all cases. For all other collections
+and arrays default behavior (with reflective serialization or `IBinaryWriter.WriteObject`) in Ignite.NET is to use `BinaryFormatter`,
+and the result can not be read by Java code (this is done to properly support generics). To write collections in interoperable
+format, implement 'IBinarizable' interface and use `IBinaryWriter.WriteCollection`, `IBinaryWriter.WriteDictionary`,
+`IBinaryReader.ReadCollection`, `IBinaryReader.ReadDictionary`methods.
+
+== Mixed-Platform Clusters
+
+Ignite, Ignite.NET and Ignite.C{pp} nodes can join the same cluster
+
+All platforms are built on top of Java, so any node can execute Java computations.
+However, .NET and C{pp} computations can be executed only by corresponding nodes.
+
+The following Ignite.NET functionality is not supported when there is at least one non-.NET node in the cluster:
+
+* Scan Queries with filter
+* Continuous Queries with filter
+* ICache.Invoke methods
+* ICache.LoadCache with filter
+* Services
+* IMessaging.RemoteListen
+* IEvents.RemoteQuery
+
+Blog post with detailed walk-through: https://ptupitsyn.github.io/Ignite-Multi-Platform-Cluster/[Multi-Platform Ignite Cluster: Java + .NET, window=_blank]
+
+== Compute in Mixed-Platform Clusters
+
+The `ICompute.ExecuteJavaTask` methods work without limitations in any cluster. Other `ICompute` methods will execute
+closures only on .NET nodes.
diff --git a/docs/_docs/net-specific/remote-assembly-loading.adoc b/docs/_docs/net-specific/remote-assembly-loading.adoc
new file mode 100644
index 0000000..3a11b29
--- /dev/null
+++ b/docs/_docs/net-specific/remote-assembly-loading.adoc
@@ -0,0 +1,140 @@
+= Remote Assembly Loading
+
+== Overview
+
+Many Ignite APIs involve remote code execution. For example, Ignite compute tasks are serialized, sent to remote nodes, and executed there.
+However, by default, .NET assemblies (DLL files) with those tasks in, must be loaded on remote nodes in order to instantiate
+and deserialize tasks' instances.
+
+Before version 2.1 you had to manually load assemblies (using `-assembly` swith with `Apache.Ignite.exe` or some other ways).
+Starting Ignite 2.1 you can take advantage of the remote assembly loading feature, that can be enabled with the
+`IgniteConfiguration.PeerAssemblyLoadingMode` flag. This configuration property needs to have the same value on all nodes
+in the cluster. Another available mode is `CurrentAppDomain`.
+
+== CurrentAppDomain Mode
+
+`PeerAssemblyLoadingMode.CurrentAppDomain` enables automatic on-demand assembly requests to other nodes in cluster,
+loading assemblies into https://msdn.microsoft.com/en-us/library/system.appdomain.aspx[AppDomain, window=_blank] where Ignite node runs.
+
+Consider the following code:
+
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+// Print Hello World on all cluster nodes.
+ignite.GetCompute().Broadcast(new HelloAction());
+
+class HelloAction : IComputeAction
+{
+  public void Invoke()
+  {
+    Console.WriteLine("Hello World!");
+  }
+}
+----
+--
+* Ignite serializes the `HelloAction` instance and broadcasts to every node in the cluster.
+* Remote nodes attempt to deserialize the `HelloAction` instance. If there is no such class in the currently loaded or referenced assemblies,
+the nodes request an assembly with the class from the node that initiated the compute task or from other nodes (if necessary).
+* The assembly file is sent from the originating or other node as a byte array and loaded with the `Assembly.Load(byte[])` method.
+
+=== Versioning
+
+https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname.aspx[Assembly-qualified type name, window=_blank]
+includes the assembly version and is used to resolve types.
+
+If you keep the cluster running, do this change in the logic and see how the assembly gets reloaded automatically:
+
+* Modify `HelloAction` intance to print something else
+* Change https://msdn.microsoft.com/en-us/library/system.reflection.assemblyversionattribute.aspx[AssemblyVersion, window=_blank]
+* Recompile and run the application code
+* The new version of the assembly will be deployed and executed on other nodes.
+
+Note, if you keep the `AssemblyVersion` unchanged, Ignite will use existing assembly that was previously loaded, since
+there are no changes in the type name.
+
+Assemblies with different versions can co-exist and be used side by side. Some nodes can continue running old code, while
+other nodes can execute computations with a newer version of the same class.
+
+The `AssemblyVersion` attribute can include asterisk (`*`) to enable the auto-increment on build: `[assembly: AssemblyVersion("1.0.*")]`.
+This way you can keep the cluster running, repeatedly modify and run computations, and new assembly versions will be deployed every time.
+
+=== Dependencies
+
+Dependent assemblies are also loaded automatically, e.g. when `ComputeAction` calls some code from a different assembly.
+Keep that in mind when using heavy frameworks and libraries: single compute call can cause lots of assemblies to be sent over the network.
+
+=== Unloading
+
+.NET does not allow assembly unloading. Instead, only the entire `AppDomain` can be unloaded with all assemblies.
+Currently available `CurrentAppDomain` mode uses existing `AppDomain`, which means all peer-deployed assemblies will stay
+loaded while current `AppDomain` lives. This may cause increased memory usage.
+
+== Example
+
+https://github.com/apache/ignite/blob/56975c266e7019f307bb9da42333a6db4e47365e/modules/platforms/dotnet/examples/Apache.Ignite.Examples/Compute/PeerAssemblyLoadingExample.cs[PeerAssemblyLoadingExample, window=_blank] can be used
+to try out the remote assembly loading feature in practice:
+
+* Create a new Console Application in Visual Studio
+* Install the Ignite.NET NuGet package `Install-Package Apache.Ignite`
+* Open the `packages\Apache.Ignite.2.1\lib\net40` folder
+* Add the `peerAssemblyLoadingMode='CurrentAppDomain'` attribute to `<igniteConfiguration>` element
+* Run `Apache.Ignite.exe` (one or more times), leave the processes running
+* Change `[AssemblyVersion]` in `AssemblyInfo.cs` to `1.0.*`
+* Modify `Program.cs` in Visual Studio as shown below
++
+[tabs]
+--
+tab:C#[]
+[source,csharp]
+----
+using System;
+using Apache.Ignite.Core;
+using Apache.Ignite.Core.Compute;
+using Apache.Ignite.Core.Deployment;
+
+namespace ConsoleApp
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            var cfg = new IgniteConfiguration
+            {
+                PeerAssemblyLoadingMode = PeerAssemblyLoadingMode.CurrentAppDomain
+            };
+
+            using (var ignite = Ignition.Start(cfg))
+            {
+                ignite.GetCompute().Broadcast(new HelloAction());
+            }
+        }
+
+        class HelloAction : IComputeAction
+        {
+            public void Invoke()
+            {
+                Console.WriteLine("Hello, World!");
+            }
+        }
+    }
+}
+----
+tab:Apache.Ignite.exe.config[]
+[source,xml]
+----
+<igniteConfiguration peerAssemblyLoadingMode='CurrentAppDomain' />
+----
+tab:AssemblyInfo.cs[]
+[source,csharp]
+----
+...
+[assembly: AssemblyVersion("1.0.*")]
+...
+----
+--
+* Run the project and observe the `"Hello, World!"` output in the console of all `Apache.Ignite.exe` windows.
+* Change the `"Hello, World!"` text to something else and run the program again
+* Observe different output on the nodes started with `Apache.Ignite.exe` earlier.