diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryRawReaderEx.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryRawReaderEx.java
index 603275ecc6809..c1785497c00b9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryRawReaderEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryRawReaderEx.java
@@ -30,4 +30,11 @@ public interface BinaryRawReaderEx extends BinaryRawReader {
      * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
      */
     @Nullable public Object readObjectDetached() throws BinaryObjectException;
+
+    /**
+     * @param deserialize {@code True} if object should be deserialized during reading.
+     * @return Object.
+     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
+     */
+    @Nullable public Object readObjectDetached(boolean deserialize) throws BinaryObjectException;
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java
index 2f0ff231059a7..8c36d34958531 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java
@@ -1333,7 +1333,12 @@ float readFloat(int fieldId) throws BinaryObjectException {
 
     /** {@inheritDoc} */
     @Nullable @Override public Object readObjectDetached() throws BinaryObjectException {
-        return BinaryUtils.unmarshal(in, ctx, ldr, this, true);
+        return readObjectDetached(false);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object readObjectDetached(boolean deserialize) throws BinaryObjectException {
+        return BinaryUtils.unmarshal(in, ctx, ldr, this, true, deserialize);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
index 84919435501b8..46267e20813e2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
@@ -1825,6 +1825,15 @@ public static Object doReadOptimized(BinaryInputStream in, BinaryContext ctx, @N
      */
     @Nullable public static Object unmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
         BinaryReaderHandlesHolder handles, boolean detach) throws BinaryObjectException {
+        return unmarshal(in, ctx, ldr, handles, detach, false);
+    }
+
+    /**
+     * @return Unmarshalled value.
+     * @throws BinaryObjectException In case of error.
+     */
+    @Nullable public static Object unmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
+        BinaryReaderHandlesHolder handles, boolean detach, boolean deserialize) throws BinaryObjectException {
         int start = in.position();
 
         byte flag = in.readByte();
@@ -1843,7 +1852,7 @@ public static Object doReadOptimized(BinaryInputStream in, BinaryContext ctx, @N
 
                     in.position(handlePos);
 
-                    obj = unmarshal(in, ctx, ldr, handles, detach);
+                    obj = unmarshal(in, ctx, ldr, handles, detach, deserialize);
 
                     in.position(retPos);
                 }
@@ -1964,13 +1973,13 @@ public static Object doReadOptimized(BinaryInputStream in, BinaryContext ctx, @N
                 return doReadTimeArray(in);
 
             case GridBinaryMarshaller.OBJ_ARR:
-                return doReadObjectArray(in, ctx, ldr, handles, detach, false);
+                return doReadObjectArray(in, ctx, ldr, handles, detach, deserialize);
 
             case GridBinaryMarshaller.COL:
-                return doReadCollection(in, ctx, ldr, handles, detach, false, null);
+                return doReadCollection(in, ctx, ldr, handles, detach, deserialize, null);
 
             case GridBinaryMarshaller.MAP:
-                return doReadMap(in, ctx, ldr, handles, detach, false, null);
+                return doReadMap(in, ctx, ldr, handles, detach, deserialize, null);
 
             case GridBinaryMarshaller.BINARY_OBJ:
                 return doReadBinaryObject(in, ctx, detach);
@@ -2162,7 +2171,7 @@ public static Collection<?> doReadCollection(BinaryInputStream in, BinaryContext
      */
     private static Object deserializeOrUnmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
         BinaryReaderHandlesHolder handles, boolean detach, boolean deserialize) {
-        return deserialize ? doReadObject(in, ctx, ldr, handles) : unmarshal(in, ctx, ldr, handles, detach);
+        return deserialize ? doReadObject(in, ctx, ldr, handles) : unmarshal(in, ctx, ldr, handles, detach, deserialize);
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/binary/PlatformBinaryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/binary/PlatformBinaryProcessor.java
index 649e7742d2771..425c29934f91d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/binary/PlatformBinaryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/binary/PlatformBinaryProcessor.java
@@ -76,10 +76,19 @@ public PlatformBinaryProcessor(PlatformContext platformCtx) {
             case OP_REGISTER_TYPE: {
                 int typeId = reader.readInt();
                 String typeName = reader.readString();
+                boolean registerSameJavaType = reader.readBoolean();
 
-                return platformContext().kernalContext().marshallerContext()
+                int res = platformContext().kernalContext().marshallerContext()
                     .registerClassName(MarshallerPlatformIds.DOTNET_ID, typeId, typeName, false)
                     ? TRUE : FALSE;
+
+                if (registerSameJavaType && res == TRUE) {
+                    res = platformContext().kernalContext().marshallerContext()
+                        .registerClassName(MarshallerPlatformIds.JAVA_ID, typeId, typeName, false)
+                        ? TRUE : FALSE;
+                }
+
+                return res;
             }
         }
 
@@ -125,10 +134,11 @@ public PlatformBinaryProcessor(PlatformContext platformCtx) {
 
             case OP_GET_TYPE: {
                 int typeId = reader.readInt();
+                byte platformId = reader.readByte();
 
                 try {
                     String typeName = platformContext().kernalContext().marshallerContext()
-                        .getClassName(MarshallerPlatformIds.DOTNET_ID, typeId);
+                        .getClassName(platformId, typeId);
 
                     writer.writeString(typeName);
                 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
index 8e67cab2d0393..d5067180d60b1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
@@ -271,6 +271,8 @@ private ServiceDescriptor findDescriptor(String name) {
                 assert arg != null;
                 assert arg instanceof ServiceProxyHolder;
 
+                ServiceProxyHolder svc = (ServiceProxyHolder)arg;
+
                 String mthdName = reader.readString();
 
                 Object[] args;
@@ -279,13 +281,13 @@ private ServiceDescriptor findDescriptor(String name) {
                     args = new Object[reader.readInt()];
 
                     for (int i = 0; i < args.length; i++)
-                        args[i] = reader.readObjectDetached();
+                        args[i] = reader.readObjectDetached(!srvKeepBinary && !svc.isPlatformService());
                 }
                 else
                     args = null;
 
                 try {
-                    Object result = ((ServiceProxyHolder)arg).invoke(mthdName, srvKeepBinary, args);
+                    Object result = svc.invoke(mthdName, srvKeepBinary, args);
 
                     PlatformUtils.writeInvocationResult(writer, result, null);
                 }
@@ -583,7 +585,7 @@ private ServiceProxyHolder(Object proxy, Class clazz, PlatformContext ctx) {
          */
         public Object invoke(String mthdName, boolean srvKeepBinary, Object[] args)
             throws IgniteCheckedException, NoSuchMethodException {
-            if (proxy instanceof PlatformService)
+            if (isPlatformService())
                 return ((PlatformService)proxy).invokeMethod(mthdName, srvKeepBinary, args);
             else {
                 assert proxy instanceof GridServiceProxy;
@@ -707,6 +709,11 @@ private static boolean areMethodArgsCompatible(Class[] argTypes, Object[] args)
         private static Class wrap(Class c) {
             return c.isPrimitive() ? PRIMITIVES_TO_WRAPPERS.get(c) : c;
         }
+
+        /** @return {@code True} if service is platform service. */
+        public boolean isPlatformService() {
+            return proxy instanceof PlatformService;
+        }
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java
index 38308229bc85b..a97934fc12466 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java
@@ -989,6 +989,9 @@ private static Collection<Object> unwrapKnownCollection(Collection<Object> col)
      * @return Result.
      */
     public static Object[] unwrapBinariesInArray(Object[] arr) {
+        if (arr.getClass().getComponentType() != Object.class)
+            return arr;
+
         Object[] res = new Object[arr.length];
 
         for (int i = 0; i < arr.length; i++)
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
index 68279385e50d0..3d476b4c485bd 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
@@ -84,6 +84,7 @@
 import org.apache.ignite.internal.managers.discovery.GridDiscoveryManager;
 import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
+import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
 import org.apache.ignite.internal.util.GridUnsafe;
 import org.apache.ignite.internal.util.IgniteUtils;
 import org.apache.ignite.internal.util.lang.GridMapEntry;
@@ -2969,7 +2970,7 @@ public void testReadDetachedMap() throws Exception {
                 assertTrue(map.containsKey(key));
                 assertEquals(val, map.get(key));
             });
-        });
+        }, false);
     }
 
     /**
@@ -2989,27 +2990,59 @@ public void testReadDetachedCollection() throws Exception {
 
                 assertTrue(col.contains(val));
             });
-        });
+        }, false);
     }
 
-    /**
-     * @throws Exception If failed.
-     */
+    /** @throws Exception If failed. */
     @Test
-    public void testReadDetachedArray() throws Exception {
+    public void testReadDetachedTypedArray() throws Exception {
         Value[] arr = IntStream.range(0, 1000).mapToObj(Value::new).toArray(Value[]::new);
 
         testReadDetachObjectProperly(arr, obj -> {
-            Object[] desArr = (Object[])obj;
+            assertArrayEquals(arr, (Value[])obj);
 
-            assertEquals(arr.length, desArr.length);
+            Object[] args = new Object[] {obj};
 
-            for (int i = 0; i < arr.length; i++) {
-                BinaryObject val = (BinaryObject)desArr[i];
+            assertTrue(args[0] instanceof Value[]);
 
-                assertEquals(arr[i], new Value(val.field("val")));
-            }
-        });
+            args = PlatformUtils.unwrapBinariesInArray(args);
+
+            assertTrue(args[0] instanceof Value[]);
+            assertArrayEquals(arr, (Value[])args[0]);
+        }, true);
+    }
+
+    /** @throws Exception If failed. */
+    @Test
+    public void testReadArrayOfCollections() throws Exception {
+        Collection[] arr = new Collection[] { Arrays.asList(new Value(1), new Value(2), new Value(3)) };
+        testReadDetachObjectProperly(arr, obj -> {
+            assertArrayEquals(arr, (Collection[])obj);
+
+            Object[] args = new Object[] {obj};
+
+            assertTrue(args[0] instanceof Collection[]);
+
+            args = PlatformUtils.unwrapBinariesInArray(args);
+
+            assertTrue(args[0] instanceof Collection[]);
+            assertArrayEquals(arr, (Collection[])args[0]);
+        }, true);
+    }
+
+
+    /** @throws Exception If failed. */
+    @Test
+    public void testReadArrayOfBinaryCollections() throws Exception {
+        Collection[] arr = new Collection[] { new ArrayList<>(Arrays.asList(new Value(1), new Value(2), new Value(3))) };
+
+        testReadDetachObjectProperly(arr, obj -> {
+            Object[] args = PlatformUtils.unwrapBinariesInArray(new Object[] {obj});
+
+            Collection deserVals = (Collection)((Object[])args[0])[0];
+
+            assertEqualsCollections(arr[0], deserVals);
+        }, false);
     }
 
     /**
@@ -3019,7 +3052,7 @@ public void testReadDetachedArray() throws Exception {
      * @param action Action to perform on object.
      * @throws Exception If failed.
      */
-    private void testReadDetachObjectProperly(Object obj, IgniteThrowableConsumer<Object> action) throws Exception {
+    private void testReadDetachObjectProperly(Object obj, IgniteThrowableConsumer<Object> action, boolean deserialize) throws Exception {
         BinaryMarshaller marsh = binaryMarshaller();
 
         BinaryHeapOutputStream os = new BinaryHeapOutputStream(1024);
@@ -3032,7 +3065,7 @@ private void testReadDetachObjectProperly(Object obj, IgniteThrowableConsumer<Ob
 
         BinaryReaderExImpl reader = marsh.binaryMarshaller().reader(is);
 
-        Object bObj = reader.readObjectDetached();
+        Object bObj = reader.readObjectDetached(deserialize);
 
         Arrays.fill(os.array(), (byte)0);
 
@@ -5507,27 +5540,6 @@ private static class DecimalMarshalAware extends DecimalReflective implements Bi
         }
     }
 
-    /**
-     * Wrapper object.
-     */
-    private static class Wrapper {
-
-        /** Value. */
-        private final Object value;
-
-        /** Constructor. */
-        public Wrapper(Object value) {
-            this.value = value;
-        }
-
-        /**
-         * @return Value.
-         */
-        public Object getValue() {
-            return value;
-        }
-    }
-
     /**
      */
     private static class SingleHandleA {
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java
index 6986ae4e80bd3..c79f8c8ff9710 100644
--- a/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java
+++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java
@@ -18,13 +18,20 @@
 package org.apache.ignite.platform;
 
 import java.sql.Timestamp;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.cluster.ClusterNode;
@@ -33,6 +40,16 @@
 import org.apache.ignite.compute.ComputeJobResult;
 import org.apache.ignite.compute.ComputeTaskAdapter;
 import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.platform.model.ACL;
+import org.apache.ignite.platform.model.Account;
+import org.apache.ignite.platform.model.Address;
+import org.apache.ignite.platform.model.Department;
+import org.apache.ignite.platform.model.Employee;
+import org.apache.ignite.platform.model.Key;
+import org.apache.ignite.platform.model.Role;
+import org.apache.ignite.platform.model.User;
+import org.apache.ignite.platform.model.Value;
 import org.apache.ignite.resources.IgniteInstanceResource;
 import org.apache.ignite.services.Service;
 import org.apache.ignite.services.ServiceContext;
@@ -89,6 +106,9 @@ private PlatformDeployServiceJob(String serviceName) {
      * Test service.
      */
     public static class PlatformTestService implements Service {
+        @IgniteInstanceResource
+        private Ignite ignite;
+
         /** */
         private boolean isCancelled;
 
@@ -412,5 +432,173 @@ public BinaryObject testBinaryObject(BinaryObject o) {
 
             return o.toBuilder().setField("field", 15).build();
         }
+
+        /** */
+        public Address testAddress(Address addr) {
+            if (addr == null)
+                return null;
+
+            assert "000".equals(addr.getZip());
+            assert "Moscow".equals(addr.getAddr());
+
+            addr.setZip("127000");
+            addr.setAddr("Moscow Akademika Koroleva 12");
+
+            return addr;
+        }
+
+        /** */
+        public int testOverload(Integer count, Employee[] emps) {
+            assert emps != null;
+            assert count == emps.length;
+
+            assert "Sarah Connor".equals(emps[0].getFio());
+            assert 1 == emps[0].getSalary();
+
+            assert "John Connor".equals(emps[1].getFio());
+            assert 2 == emps[1].getSalary();
+
+            return 42;
+        }
+
+        /** */
+        public int testOverload(int first, int second) {
+            return first + second;
+        }
+
+        /** */
+        public Employee[] testEmployees(Employee[] emps) {
+            if (emps == null)
+                return null;
+
+            assert 2 == emps.length;
+
+            assert "Sarah Connor".equals(emps[0].getFio());
+            assert 1 == emps[0].getSalary();
+
+            assert "John Connor".equals(emps[1].getFio());
+            assert 2 == emps[1].getSalary();
+
+            Employee kyle = new Employee();
+
+            kyle.setFio("Kyle Reese");
+            kyle.setSalary(3);
+
+            return new Employee[] { kyle };
+        }
+
+        /** */
+        public Collection testDepartments(Collection deps) {
+            if (deps == null)
+                return null;
+
+            assert 2 == deps.size();
+
+            Iterator<Department> iter = deps.iterator();
+
+            assert "HR".equals(iter.next().getName());
+            assert "IT".equals(iter.next().getName());
+
+            Collection<Department> res = new ArrayList<>();
+
+            Department d = new Department();
+
+            d.setName("Executive");
+
+            res.add(d);
+
+            return res;
+        }
+
+        /** */
+        public Map testMap(Map map) {
+            if (map == null)
+                return null;
+
+            assert map.containsKey(new Key(1));
+            assert map.containsKey(new Key(2));
+
+            assert "value1".equals(((Value)map.get(new Key(1))).getVal());
+            assert "value2".equals(((Value)map.get(new Key(2))).getVal());
+
+            Map m = new HashMap();
+
+            m.put(new Key(3), new Value("value3"));
+
+            return m;
+        }
+
+        /** */
+        public Account[] testAccounts() {
+            return new Account[] {
+                new Account("123", 42),
+                new Account("321", 0)
+            };
+        }
+
+        /** */
+        public User[] testUsers() {
+            return new User[] {
+                new User(1, ACL.ALLOW, new Role("admin")),
+                new User(2, ACL.DENY, new Role("user"))
+            };
+        }
+
+        /** */
+        public void testDateArray(Timestamp[] dates) {
+            assert dates != null;
+            assert 2 == dates.length;
+            assert new Timestamp(new Date(82, Calendar.APRIL, 1, 0, 0, 0).getTime()).equals(dates[0]);
+            assert new Timestamp(new Date(91, Calendar.OCTOBER, 1, 0, 0, 0).getTime()).equals(dates[1]);
+        }
+
+        /** */
+        public Timestamp testDate(Timestamp date) {
+            if (date == null)
+                return null;
+
+            assert new Timestamp(new Date(82, Calendar.APRIL, 1, 0, 0, 0).getTime()).equals(date);
+
+            return new Timestamp(new Date(91, Calendar.OCTOBER, 1, 0, 0, 0).getTime());
+        }
+
+        /** */
+        public void testUTCDateFromCache() {
+            IgniteCache<Integer, Timestamp> cache = ignite.cache("net-dates");
+
+            cache.put(3, new Timestamp(new Date(82, Calendar.APRIL, 1, 0, 0, 0).getTime()));
+            cache.put(4, new Timestamp(new Date(91, Calendar.OCTOBER, 1, 0, 0, 0).getTime()));
+
+            assert new Timestamp(new Date(82, Calendar.APRIL, 1, 0, 0, 0).getTime()).equals(cache.get(1));
+            assert new Timestamp(new Date(91, Calendar.OCTOBER, 1, 0, 0, 0).getTime()).equals(cache.get(2));
+        }
+
+        /** */
+        public void testLocalDateFromCache() {
+            IgniteCache<Integer, Timestamp> cache = ignite.cache("net-dates");
+
+            ZoneId msk = ZoneId.of("Europe/Moscow");
+
+            //This Date in Europe/Moscow have offset +4.
+            Timestamp ts1 = new Timestamp(ZonedDateTime.of(1982, 4, 1, 1, 0, 0, 0, msk).toInstant().toEpochMilli());
+            //This Date in Europe/Moscow have offset +3.
+            Timestamp ts2 = new Timestamp(ZonedDateTime.of(1982, 3, 31, 22, 0, 0, 0, msk).toInstant().toEpochMilli());
+
+            assert ts1.equals(cache.get(5));
+            assert ts2.equals(cache.get(6));
+
+            cache.put(7, ts1);
+            cache.put(8, ts2);
+        }
+
+        /** */
+        public void sleep(long delayMs) {
+            try {
+                U.sleep(delayMs);
+            }
+            catch (Exception e) {
+                throw new IgniteException(e);
+            }
+        }
     }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/ACL.java b/modules/core/src/test/java/org/apache/ignite/platform/model/ACL.java
new file mode 100644
index 0000000000000..6f75846186ba2
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/model/ACL.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.platform.model;
+
+/** Test enum. */
+public enum ACL {
+    ALLOW, DENY
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Account.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Account.java
new file mode 100644
index 0000000000000..d192ce77035e8
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Account.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.platform.model;
+
+import java.util.Objects;
+
+/** */
+public class Account {
+    /** */
+    private String id;
+
+    /** */
+    private int amount;
+
+    /** */
+    public Account() {
+    }
+
+    public Account(String id, int amount) {
+        this.id = id;
+        this.amount = amount;
+    }
+
+    /** */
+    public String getId() {
+        return id;
+    }
+
+    /** */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /** */
+    public int getAmount() {
+        return amount;
+    }
+
+    /** */
+    public void setAmount(int amount) {
+        this.amount = amount;
+    }
+
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        Account account = (Account)o;
+        return Objects.equals(id, account.id);
+    }
+
+    @Override public int hashCode() {
+        return Objects.hash(id);
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Address.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Address.java
new file mode 100644
index 0000000000000..42442cfcebd5d
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Address.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.platform.model;
+
+/** Test value object. */
+public class Address {
+    /** */
+    private String zip;
+
+    /** */
+    private String addr;
+
+    /** */
+    public String getZip() {
+        return zip;
+    }
+
+    /** */
+    public void setZip(String zip) {
+        this.zip = zip;
+    }
+
+    /** */
+    public String getAddr() {
+        return addr;
+    }
+
+    /** */
+    public void setAddr(String addr) {
+        this.addr = addr;
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Department.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Department.java
new file mode 100644
index 0000000000000..b1f74f3731e0a
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Department.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.platform.model;
+
+/** Test value object. */
+public class Department {
+    /** */
+    private String name;
+
+    /** */
+    public String getName() {
+        return name;
+    }
+
+    /** */
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Employee.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Employee.java
new file mode 100644
index 0000000000000..6c63c29d684b1
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Employee.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.platform.model;
+
+/** Test value object. */
+public class Employee {
+    /** */
+    private String fio;
+
+    /** */
+    private long salary;
+
+    /** */
+    public String getFio() {
+        return fio;
+    }
+
+    /** */
+    public void setFio(String fio) {
+        this.fio = fio;
+    }
+
+    /** */
+    public long getSalary() {
+        return salary;
+    }
+
+    /** */
+    public void setSalary(long salary) {
+        this.salary = salary;
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Key.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Key.java
new file mode 100644
index 0000000000000..218d9c0b1e1e8
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Key.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.platform.model;
+
+import java.util.Objects;
+
+/** Test key object. */
+public class Key {
+    /** */
+    private long id;
+
+    /** */
+    public Key(long id) {
+        this.id = id;
+    }
+
+    /** */
+    public long getId() {
+        return id;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        Key key = (Key)o;
+        return id == key.id;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return Objects.hash(id);
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Role.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Role.java
new file mode 100644
index 0000000000000..f47eddb2b6fa6
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Role.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.platform.model;
+
+/** Test value object. */
+public class Role {
+    /** */
+    String name;
+
+    /** */
+    public Role(String name) {
+        this.name = name;
+    }
+
+    /** */
+    public String getName() {
+        return name;
+    }
+
+    /** */
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/User.java b/modules/core/src/test/java/org/apache/ignite/platform/model/User.java
new file mode 100644
index 0000000000000..88849d16d658c
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/model/User.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.platform.model;
+
+/** Test value object. */
+public class User {
+    /** */
+    private int id;
+
+    /** */
+    private ACL acl;
+
+    /** */
+    private Role role;
+
+    /** */
+    public User(int id, ACL acl, Role role) {
+        this.id = id;
+        this.acl = acl;
+        this.role = role;
+    }
+
+    /** */
+    public int getId() {
+        return id;
+    }
+
+    /** */
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    /** */
+    public ACL getAcl() {
+        return acl;
+    }
+
+    /** */
+    public void setAcl(ACL acl) {
+        this.acl = acl;
+    }
+
+    /** */
+    public Role getRole() {
+        return role;
+    }
+
+    /** */
+    public void setRole(Role role) {
+        this.role = role;
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/model/Value.java b/modules/core/src/test/java/org/apache/ignite/platform/model/Value.java
new file mode 100644
index 0000000000000..7143bff97cc48
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/model/Value.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.platform.model;
+
+import java.util.Objects;
+
+/** Test value object. */
+public class Value {
+    /** */
+    private String val;
+
+    /** */
+    public Value(String val) {
+        this.val = val;
+    }
+
+    /** */
+    public String getVal() {
+        return val;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        Value value = (Value)o;
+        return Objects.equals(val, value.val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return Objects.hash(val);
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
index e745e90d924be..10dfa37ccfa31 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
@@ -343,11 +343,15 @@
     <Compile Include="..\Apache.Ignite.Core.Tests\Plugin\TestIgnitePluginException.cs" Link="Plugin\TestIgnitePluginException.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Plugin\TestIgnitePluginProvider.cs" Link="Plugin\TestIgnitePluginProvider.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Query\BinarizablePerson.cs" Link="Cache\Query\BinarizablePerson.cs" />
+    <Compile Include="..\Apache.Ignite.Core.Tests\Services\Model.cs" Link="Services\Model.cs" />
+    <Compile Include="..\Apache.Ignite.Core.Tests\Services\IJavaService.cs" Link="Services\IJavaService.cs" />
+    <Compile Include="..\Apache.Ignite.Core.Tests\Services\JavaServiceDynamicProxy.cs" Link="Services\JavaServiceDynamicProxy.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Services\CallPlatformServiceTest.cs" Link="Services\CallPlatformServiceTest.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServiceProxyTest.cs" Link="Services\ServiceProxyTest.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServicesAsyncWrapper.cs" Link="Services\ServicesAsyncWrapper.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServicesTest.cs" Link="Services\ServicesTest.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServicesTestAsync.cs" Link="Services\ServicesTestAsync.cs" />
+    <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServiceTypeAutoResolveTest.cs" Link="Services\ServiceTypeAutoResolveTest.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\TaskExtensions.cs" Link="Common\TaskExtensions.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\TestUtils.Common.cs" Link="Common\TestUtils.Common.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\EnvVar.cs" Link="Common\EnvVar.cs" />
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
index 9779ddaddd028..4fccb7503174d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
@@ -262,6 +262,9 @@
     <Compile Include="Plugin\TestIgnitePluginConfiguration.cs" />
     <Compile Include="Plugin\TestIgnitePluginException.cs" />
     <Compile Include="Plugin\TestIgnitePluginProvider.cs" />
+    <Compile Include="Services\Model.cs" />
+    <Compile Include="Services\IJavaService.cs" />
+    <Compile Include="Services\JavaServiceDynamicProxy.cs" />
     <Compile Include="Ssl\SslConfigurationTest.cs" />
     <Compile Include="TaskExtensions.cs" />
     <Compile Include="TestAppConfig.cs" />
@@ -374,6 +377,7 @@
     <Compile Include="Services\ServicesTestAsync.cs" />
     <Compile Include="Services\ServiceProxyTest.cs" />
     <Compile Include="Services\ServicesAsyncWrapper.cs" />
+    <Compile Include="Services\ServiceTypeAutoResolveTest.cs" />
     <Compile Include="TestRunner.cs" />
     <Compile Include="Unmanaged\JniThreadDetachTest.cs" />
     <Compile Include="Unmanaged\UnmanagedThreadTest.cs" />
@@ -587,4 +591,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
\ No newline at end of file
+</Project>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs
index e971eeac04b3c..9eae89307d24e 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs
@@ -21,6 +21,7 @@ namespace Apache.Ignite.Core.Tests.Binary
     using System.Linq;
     using Apache.Ignite.Core.Binary;
     using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Impl.Binary;
     using NUnit.Framework;
 
     /// <summary>
@@ -28,6 +29,16 @@ namespace Apache.Ignite.Core.Tests.Binary
     /// </summary>
     public class BinaryDateTimeTest
     {
+        /** */
+        internal const String FromErrMsg = "FromJavaTicks Error!";
+
+        /** */
+        internal const String ToErrMsg = "ToJavaTicks Error!";
+
+        /** */
+        private const String NotUtcDate =
+            "DateTime is not UTC. Only UTC DateTime can be used for interop with other platforms.";
+
         /// <summary>
         /// Sets up the test fixture.
         /// </summary>
@@ -80,7 +91,7 @@ public void TestSerializerForceTimestamp()
                 .Select(x => x.Serializer)
                 .OfType<BinaryReflectiveSerializer>()
                 .Single();
-            
+
             Assert.IsTrue(ser.ForceTimestamp);
 
             AssertTimestampField<DateTimeObj2>((o, d) => o.Value = d, o => o.Value, "Value");
@@ -110,6 +121,152 @@ public void TestClassAttributes()
             AssertTimestampField<DateTimeClassAttribute2>((o, d) => o.Value = d, o => o.Value, "Value");
         }
 
+        /// <summary>
+        /// Tests custom timestamp converter that modifies the values by adding one year on write and read.
+        /// This test verifies that actual converted values are used by Ignite.
+        /// </summary>
+        [Test]
+        public void TestAddYearTimestampConverter()
+        {
+            var cfg =  new IgniteConfiguration(TestUtils.GetTestConfiguration())
+            {
+                AutoGenerateIgniteInstanceName = true,
+                BinaryConfiguration = new BinaryConfiguration
+                {
+                    ForceTimestamp = true, 
+                    TimestampConverter = new AddYearTimestampConverter()
+                }
+            };
+
+            var ignite = Ignition.Start(cfg);
+
+            var dt = DateTime.UtcNow;
+            var expected = dt.AddYears(2);
+
+            // Key & value.
+            var cache = ignite.GetOrCreateCache<DateTime, DateTime>(TestUtils.TestName);
+            cache[dt] = dt;
+
+            var resEntry = cache.Single();
+            
+            Assert.AreEqual(expected, resEntry.Key);
+            Assert.AreEqual(expected, resEntry.Value);
+            
+            // Key & value array.
+            
+            // Object field.
+            var cache2 = ignite.GetOrCreateCache<DateTimePropertyAttribute, DateTimePropertyAttribute>(
+                TestUtils.TestName);
+            
+            cache2.RemoveAll();
+            
+            var obj = new DateTimePropertyAttribute {Value = dt};
+            cache2[obj] = obj;
+
+            var resEntry2 = cache2.Single();
+            Assert.AreEqual(expected, resEntry2.Key.Value);
+            Assert.AreEqual(expected, resEntry2.Value.Value);
+            
+            // Object array field.
+            var cache3 = ignite.GetOrCreateCache<DateTimeArr, DateTimeArr>(TestUtils.TestName);
+            cache3.RemoveAll();
+            
+            var obj2 = new DateTimeArr {Value = new[]{dt}};
+            cache3[obj2] = obj2;
+            cache3[obj2] = obj2;
+
+            var resEntry3 = cache3.Single();
+            Assert.AreEqual(expected, resEntry3.Key.Value.Single());
+            Assert.AreEqual(expected, resEntry3.Value.Value.Single());
+        }
+
+        /// <summary>
+        /// Tests custom timestamp converter.
+        /// </summary>
+        [Test]
+        public void TestCustomTimestampConverter()
+        {
+            var cfg =  new IgniteConfiguration(TestUtils.GetTestConfiguration(name: "ignite-1"))
+            {
+                BinaryConfiguration = new BinaryConfiguration
+                {
+                    ForceTimestamp = true, 
+                    TimestampConverter = new TimestampConverter()
+                }
+            };
+
+            var ignite = Ignition.Start(cfg);
+            var binary = ignite.GetBinary();
+ 
+            // Check config.
+            Assert.NotNull(ignite.GetConfiguration().BinaryConfiguration.TimestampConverter);
+
+            AssertTimestampField<DateTimeObj2>((o, d) => o.Value = d, o => o.Value, "Value", ignite);
+
+            var dt1 = new DateTime(1997, 8, 29, 0, 0, 0, DateTimeKind.Utc);
+
+            var ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime>(dt1));
+            Assert.AreEqual(ToErrMsg, ex.Message);
+
+            ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime?[]>(new DateTime?[] {dt1}));
+            Assert.AreEqual(ToErrMsg, ex.Message);
+
+            var dt2 = new DateTime(1997, 8, 4, 0, 0, 0, DateTimeKind.Utc);
+
+            ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime>(dt2));
+            Assert.AreEqual(FromErrMsg, ex.Message);
+
+            ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime?[]>(new DateTime?[] {dt2}));
+            Assert.AreEqual(FromErrMsg, ex.Message);
+
+            var datesCache = ignite.CreateCache<DateTime, DateTime>("dates");
+
+            var check = new Action<DateTime, DateTime, String>((date1, date2, errMsg) =>
+            {
+                ex = Assert.Throws<BinaryObjectException>(() => datesCache.Put(date1, date2), "Timestamp fields should throw an error on non-UTC values");
+
+                Assert.AreEqual(errMsg, ex.Message);
+            });
+
+            check.Invoke(DateTime.Now, DateTime.UtcNow, NotUtcDate);
+            check.Invoke(DateTime.UtcNow, DateTime.Now, NotUtcDate);
+            check.Invoke(dt1, DateTime.UtcNow, ToErrMsg);
+            check.Invoke(DateTime.UtcNow, dt1, ToErrMsg);
+
+            var now = DateTime.UtcNow;
+
+            datesCache.Put(now, dt2);
+            ex = Assert.Throws<BinaryObjectException>(() => datesCache.Get(now), "Timestamp fields should throw an error on non-UTC values");
+            Assert.AreEqual(FromErrMsg, ex.Message);
+            
+            datesCache.Put(now, now);
+            Assert.AreEqual(now, datesCache.Get(now));
+
+            var datesObjCache = ignite.CreateCache<DateTimeObj, DateTimeObj>("datesObj");
+
+            check = (date1, date2, errMsg) =>
+            {
+                ex = Assert.Throws<BinaryObjectException>(() => datesObjCache.Put(new DateTimeObj {Value = date1}, new DateTimeObj {Value = date2}),
+                    "Timestamp fields should throw an error on non-UTC values");
+
+                Assert.AreEqual(errMsg, ex.Message);
+            };
+
+            check.Invoke(DateTime.Now, DateTime.UtcNow, NotUtcDate);
+            check.Invoke(DateTime.UtcNow, DateTime.Now, NotUtcDate);
+            check.Invoke(dt1, DateTime.UtcNow, ToErrMsg);
+            check.Invoke(DateTime.UtcNow, dt1, ToErrMsg);
+
+            var nowObj = new DateTimeObj {Value = now};
+
+            datesObjCache.Put(nowObj, new DateTimeObj {Value = dt2});
+            ex = Assert.Throws<BinaryObjectException>(() => datesObjCache.Get(nowObj), "Timestamp fields should throw an error on non-UTC values");
+            Assert.AreEqual(FromErrMsg, ex.Message);
+            
+            datesObjCache.Put(nowObj, nowObj);
+            Assert.AreEqual(nowObj.Value, datesObjCache.Get(nowObj).Value);
+        }
+
         /// <summary>
         /// Asserts that specified field is serialized as DateTime object.
         /// </summary>
@@ -136,10 +293,10 @@ private static void AssertDateTimeField<T>(Action<T, DateTime> setValue,
         /// Asserts that specified field is serialized as Timestamp.
         /// </summary>
         private static void AssertTimestampField<T>(Action<T, DateTime> setValue,
-            Func<T, DateTime> getValue, string fieldName) where T : new()
+            Func<T, DateTime> getValue, string fieldName, IIgnite ignite = null) where T : new()
         {
             // Non-UTC DateTime throws.
-            var binary = Ignition.GetIgnite().GetBinary();
+            var binary = ignite != null ? ignite.GetBinary() : Ignition.GetIgnite().GetBinary();
 
             var obj = new T();
 
@@ -148,8 +305,7 @@ private static void AssertTimestampField<T>(Action<T, DateTime> setValue,
             var ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<IBinaryObject>(obj), 
                 "Timestamp fields should throw an error on non-UTC values");
 
-            Assert.AreEqual("DateTime is not UTC. Only UTC DateTime can be used for interop with other platforms.",
-                ex.Message);
+            Assert.AreEqual(NotUtcDate, ex.Message);
 
             // UTC DateTime works.
             setValue(obj, DateTime.UtcNow);
@@ -171,6 +327,11 @@ private class DateTimeObj2
             public DateTime Value { get; set; }
         }
 
+        private class DateTimeArr
+        {
+            public DateTime[] Value { get; set; }
+        }
+
         private class DateTimePropertyAttribute
         {
             [Timestamp]
@@ -200,5 +361,47 @@ private class DateTimeClassAttribute2
         {
             public DateTime Value;
         }
+        
+        private class TimestampConverter : ITimestampConverter
+        {
+            /** <inheritdoc /> */
+            public void ToJavaTicks(DateTime date, out long high, out int low)
+            {
+                if (date.Year == 1997 && date.Month == 8 && date.Day == 29)
+                    throw new BinaryObjectException(BinaryDateTimeTest.ToErrMsg);
+
+                BinaryUtils.ToJavaDate(date, out high, out low);
+            }
+
+            /** <inheritdoc /> */
+            public DateTime FromJavaTicks(long high, int low)
+            {
+                var date = new DateTime(BinaryUtils.JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100,
+                    DateTimeKind.Utc);
+
+                if (date.Year == 1997 && date.Month == 8 && date.Day == 4)
+                    throw new BinaryObjectException(BinaryDateTimeTest.FromErrMsg);
+
+                return date;
+            }
+        }
+        
+        private class AddYearTimestampConverter : ITimestampConverter
+        {
+            /** <inheritdoc /> */
+            public void ToJavaTicks(DateTime date, out long high, out int low)
+            {
+                BinaryUtils.ToJavaDate(date.AddYears(1), out high, out low);
+            }
+
+            /** <inheritdoc /> */
+            public DateTime FromJavaTicks(long high, int low)
+            {
+                var date = new DateTime(BinaryUtils.JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100,
+                    DateTimeKind.Utc);
+
+                return date.AddYears(1);
+            }
+        }
     }
 }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs
index 98c69f607d20a..e152c6b94c5c3 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs
@@ -104,7 +104,7 @@ public void PutBinaryTypes(ICollection<BinaryType> types)
             }
 
             /** <inheritdoc /> */
-            public bool RegisterType(int id, string typeName)
+            public bool RegisterType(int id, string typeName, bool registerSameJavaType)
             {
                 return false;
             }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs
new file mode 100644
index 0000000000000..11a13b88ad86a
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Tests.Services
+{
+    using System;
+    using System.Collections;
+    using System.Collections.Generic;
+    using System.Diagnostics.CodeAnalysis;
+    using Apache.Ignite.Core.Binary;
+    using org.apache.ignite.platform.model;
+
+    /// <summary>
+    /// Java service proxy interface.
+    /// </summary>
+    [SuppressMessage("ReSharper", "InconsistentNaming")]
+    public interface IJavaService
+    {
+        /** */
+        bool isCancelled();
+
+        /** */
+        bool isInitialized();
+
+        /** */
+        bool isExecuted();
+
+        /** */
+        byte test(byte x);
+
+        /** */
+        short test(short x);
+
+        /** */
+        int test(int x);
+
+        /** */
+        long test(long x);
+
+        /** */
+        float test(float x);
+
+        /** */
+        double test(double x);
+
+        /** */
+        char test(char x);
+
+        /** */
+        string test(string x);
+
+        /** */
+        bool test(bool x);
+
+        /** */
+        DateTime test(DateTime x);
+
+        /** */
+        Guid test(Guid x);
+
+        /** */
+        byte? testWrapper(byte? x);
+
+        /** */
+        short? testWrapper(short? x);
+
+        /** */
+        int? testWrapper(int? x);
+
+        /** */
+        long? testWrapper(long? x);
+
+        /** */
+        float? testWrapper(float? x);
+
+        /** */
+        double? testWrapper(double? x);
+
+        /** */
+        char? testWrapper(char? x);
+
+        /** */
+        bool? testWrapper(bool? x);
+
+        /** */
+        byte[] testArray(byte[] x);
+
+        /** */
+        short[] testArray(short[] x);
+
+        /** */
+        int[] testArray(int[] x);
+
+        /** */
+        long[] testArray(long[] x);
+
+        /** */
+        float[] testArray(float[] x);
+
+        /** */
+        double[] testArray(double[] x);
+
+        /** */
+        char[] testArray(char[] x);
+
+        /** */
+        string[] testArray(string[] x);
+
+        /** */
+        bool[] testArray(bool[] x);
+
+        /** */
+        DateTime?[] testArray(DateTime?[] x);
+
+        /** */
+        Guid?[] testArray(Guid?[] x);
+
+        /** */
+        int test(int x, string y);
+
+        /** */
+        int test(string x, int y);
+
+        /** */
+        int? testNull(int? x);
+
+        /** */
+        DateTime? testNullTimestamp(DateTime? x);
+
+        /** */
+        Guid? testNullUUID(Guid? x);
+
+        /** */
+        int testParams(params object[] args);
+
+        /** */
+        ServicesTest.PlatformComputeBinarizable testBinarizable(ServicesTest.PlatformComputeBinarizable x);
+
+        /** */
+        object[] testBinarizableArrayOfObjects(object[] x);
+
+        /** */
+        IBinaryObject[] testBinaryObjectArray(IBinaryObject[] x);
+
+        /** */
+        ServicesTest.PlatformComputeBinarizable[] testBinarizableArray(ServicesTest.PlatformComputeBinarizable[] x);
+
+        /** */
+        ICollection testBinarizableCollection(ICollection x);
+
+        /** */
+        IBinaryObject testBinaryObject(IBinaryObject x);
+
+        /** */
+        Address testAddress(Address addr);
+
+        /** */
+        int testOverload(int count, Employee[] emps);
+
+        /** */
+        int testOverload(int first, int second);
+
+        /** */
+        Employee[] testEmployees(Employee[] emps);
+        
+        /** */
+        Account[] testAccounts();
+
+        /** */
+        User[] testUsers();
+
+        /** */
+        ICollection testDepartments(ICollection deps);
+
+        /** */
+        IDictionary testMap(IDictionary<Key, Value> dict);
+
+        /** */
+        void testDateArray(DateTime?[] dates);
+
+        /** */
+        DateTime testDate(DateTime date);
+
+        /** */
+        void testUTCDateFromCache();
+
+        /** */
+        void testLocalDateFromCache();
+
+        /** */
+        void sleep(long delayMs);
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs
new file mode 100644
index 0000000000000..3f34366b6e00a
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Tests.Services
+{
+    using System;
+    using System.Collections;
+    using System.Collections.Generic;
+    using Apache.Ignite.Core.Binary;
+    using org.apache.ignite.platform.model;
+
+    /// <summary>
+    /// Explicit service proxy over dynamic variable.
+    /// </summary>
+    public class JavaServiceDynamicProxy : IJavaService
+    {
+        /** */
+        private readonly dynamic _svc;
+
+        /** */
+        public JavaServiceDynamicProxy(dynamic svc)
+        {
+            _svc = svc;
+        } 
+
+        /** <inheritDoc /> */
+        public bool isCancelled()
+        {
+            return _svc.isCancelled();
+        }
+
+        /** <inheritDoc /> */
+        public bool isInitialized()
+        {
+            return _svc.isInitialized();
+        }
+
+        /** <inheritDoc /> */
+        public bool isExecuted()
+        {
+            return _svc.isExecuted();
+        }
+
+        /** <inheritDoc /> */
+        public byte test(byte x)
+        {
+            return _svc.test(x);
+        }
+
+        /** <inheritDoc /> */
+        public short test(short x)
+        {
+            return _svc.test(x);
+        }
+
+        /** <inheritDoc /> */
+        public int test(int x)
+        {
+            return _svc.test(x);
+        }
+
+        /** <inheritDoc /> */
+        public long test(long x)
+        {
+            return _svc.test(x);
+        }
+
+        /** <inheritDoc /> */
+        public float test(float x)
+        {
+            return _svc.test(x);
+        }
+
+        /** <inheritDoc /> */
+        public double test(double x)
+        {
+            return _svc.test(x);
+        }
+
+        /** <inheritDoc /> */
+        public char test(char x)
+        {
+            return _svc.test(x);
+        }
+
+        /** <inheritDoc /> */
+        public string test(string x)
+        {
+            return _svc.test(x);
+        }
+
+        /** <inheritDoc /> */
+        public bool test(bool x)
+        {
+            return _svc.test(x);
+        }
+
+        /** <inheritDoc /> */
+        public DateTime test(DateTime x)
+        {
+            return _svc.test(x);
+        }
+
+        /** <inheritDoc /> */
+        public Guid test(Guid x)
+        {
+            return _svc.test(x);
+        }
+
+        /** <inheritDoc /> */
+        public byte? testWrapper(byte? x)
+        {
+            return _svc.testWrapper(x);
+        }
+
+        /** <inheritDoc /> */
+        public short? testWrapper(short? x)
+        {
+            return _svc.testWrapper(x);
+        }
+
+        /** <inheritDoc /> */
+        public int? testWrapper(int? x)
+        {
+            return _svc.testWrapper(x);
+        }
+
+        /** <inheritDoc /> */
+        public long? testWrapper(long? x)
+        {
+            return _svc.testWrapper(x);
+        }
+
+        /** <inheritDoc /> */
+        public float? testWrapper(float? x)
+        {
+            return _svc.testWrapper(x);
+        }
+
+        /** <inheritDoc /> */
+        public double? testWrapper(double? x)
+        {
+            return _svc.testWrapper(x);
+        }
+
+        /** <inheritDoc /> */
+        public char? testWrapper(char? x)
+        {
+            return _svc.testWrapper(x);
+        }
+
+        /** <inheritDoc /> */
+        public bool? testWrapper(bool? x)
+        {
+            return _svc.testWrapper(x);
+        }
+
+        /** <inheritDoc /> */
+        public byte[] testArray(byte[] x)
+        {
+            return _svc.testArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public short[] testArray(short[] x)
+        {
+            return _svc.testArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public int[] testArray(int[] x)
+        {
+            return _svc.testArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public long[] testArray(long[] x)
+        {
+            return _svc.testArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public float[] testArray(float[] x)
+        {
+            return _svc.testArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public double[] testArray(double[] x)
+        {
+            return _svc.testArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public char[] testArray(char[] x)
+        {
+            return _svc.testArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public string[] testArray(string[] x)
+        {
+            return _svc.testArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public bool[] testArray(bool[] x)
+        {
+            return _svc.testArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public DateTime?[] testArray(DateTime?[] x)
+        {
+            return _svc.testArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public Guid?[] testArray(Guid?[] x)
+        {
+            return _svc.testArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public int test(int x, string y)
+        {
+            return _svc.test(x, y);
+        }
+
+        /** <inheritDoc /> */
+        public int test(string x, int y)
+        {
+            return _svc.test(x, y);
+        }
+
+        /** <inheritDoc /> */
+        public int? testNull(int? x)
+        {
+            return _svc.testNull(x);
+        }
+
+        /** <inheritDoc /> */
+        public DateTime? testNullTimestamp(DateTime? x)
+        {
+            return _svc.testNullTimestamp(x);
+        }
+
+        /** <inheritDoc /> */
+        public Guid? testNullUUID(Guid? x)
+        {
+            return _svc.testNullUUID(x);
+        }
+
+        /** <inheritDoc /> */
+        public int testParams(params object[] args)
+        {
+            return _svc.testParams(args);
+        }
+
+        /** <inheritDoc /> */
+        public ServicesTest.PlatformComputeBinarizable testBinarizable(ServicesTest.PlatformComputeBinarizable x)
+        {
+            return _svc.testBinarizable(x);
+        }
+
+        /** <inheritDoc /> */
+        public object[] testBinarizableArrayOfObjects(object[] x)
+        {
+            return _svc.testBinarizableArrayOfObjects(x);
+        }
+
+        /** <inheritDoc /> */
+        public IBinaryObject[] testBinaryObjectArray(IBinaryObject[] x)
+        {
+            return _svc.testBinaryObjectArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public ServicesTest.PlatformComputeBinarizable[] testBinarizableArray(ServicesTest.PlatformComputeBinarizable[] x)
+        {
+            return _svc.testBinarizableArray(x);
+        }
+
+        /** <inheritDoc /> */
+        public ICollection testBinarizableCollection(ICollection x)
+        {
+            return _svc.testBinarizableCollection(x);
+        }
+
+        /** <inheritDoc /> */
+        public IBinaryObject testBinaryObject(IBinaryObject x)
+        {
+            return _svc.testBinaryObject(x);
+        }
+
+        /** <inheritDoc /> */
+        public Address testAddress(Address addr)
+        {
+            return _svc.testAddress(addr);
+        }
+
+        /** <inheritDoc /> */
+        public int testOverload(int count, Employee[] emps)
+        {
+            return _svc.testOverload(count, emps);
+        }
+
+        /** <inheritDoc /> */
+        public int testOverload(int first, int second)
+        {
+            return _svc.testOverload(first, second);
+        }
+
+        /** <inheritDoc /> */
+        public Employee[] testEmployees(Employee[] emps)
+        {
+            return _svc.testEmployees(emps);
+        }
+
+        public Account[] testAccounts()
+        {
+            return _svc.testAccounts();
+        }
+
+        public User[] testUsers()
+        {
+            return _svc.testUsers();
+        }
+
+        /** <inheritDoc /> */
+        public ICollection testDepartments(ICollection deps)
+        {
+            return _svc.testDepartments(deps);
+        }
+
+        /** <inheritDoc /> */
+        public IDictionary testMap(IDictionary<Key, Value> dict)
+        {
+            return _svc.testMap(dict);
+        }
+
+        /** <inheritDoc /> */
+        public void testDateArray(DateTime?[] dates)
+        {
+            _svc.testDateArray(dates);
+        }
+
+        /** <inheritDoc /> */
+        public DateTime testDate(DateTime date)
+        {
+            return _svc.testDate(date);
+        }
+
+        /** <inheritDoc /> */
+        public void testUTCDateFromCache()
+        {
+            _svc.testDateFromCache();
+        }
+
+        /** <inheritDoc /> */
+        public void testLocalDateFromCache()
+        {
+            _svc.testLocalDateFromCache();
+        }
+
+        /** <inheritDoc /> */
+        public void sleep(long delayMs)
+        {
+            _svc.sleep(delayMs);
+        }
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs
new file mode 100644
index 0000000000000..275dc7dace2ec
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// ReSharper disable once CheckNamespace
+namespace org.apache.ignite.platform.model
+{
+    using System;
+
+    /// <summary>
+    /// A class is a clone of Java class Address with the same namespace.
+    /// </summary>
+    public class Address
+    {
+        /** */
+        public string Zip { get; set; }
+
+        /** */
+        public string Addr { get; set; }
+    }
+
+    /// <summary>
+    /// A class is a clone of Java class Department with the same namespace.
+    /// </summary>
+    public class Department
+    {
+        /** */
+        public string Name { get; set; }
+    }
+
+    /// <summary>
+    /// A class is a clone of Java class Employee with the same namespace.
+    /// </summary>
+    public class Employee
+    {
+        /** */
+        public string Fio { get; set; }
+
+        /** */
+        public long Salary { get; set; }
+    }
+
+    /// <summary>
+    /// A class is a clone of Java class Employee with the same namespace.
+    /// </summary>
+    public class Key
+    {
+        public long Id { get; set; }
+
+        protected bool Equals(Key other)
+        {
+            return Id == other.Id;
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            if (ReferenceEquals(this, obj)) return true;
+            if (obj.GetType() != this.GetType()) return false;
+            return Equals((Key) obj);
+        }
+
+        public override int GetHashCode()
+        {
+            // ReSharper disable once NonReadonlyMemberInGetHashCode
+            return Id.GetHashCode();
+        }
+    }
+
+    /// <summary>
+    /// A class is a clone of Java class Employee with the same namespace.
+    /// </summary>
+    public class Value
+    {
+        public string Val { get; set; }
+    }
+
+    /// <summary>
+    /// A class is a clone of Java class Account with the same namespace.
+    /// </summary>
+    public class Account
+    {
+        public String Id { get; set; }
+        
+        public int Amount { get; set; }
+
+        protected bool Equals(Account other)
+        {
+            return Id == other.Id;
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            if (ReferenceEquals(this, obj)) return true;
+            if (obj.GetType() != this.GetType()) return false;
+            return Equals((Account) obj);
+        }
+
+        public override int GetHashCode()
+        {
+            // ReSharper disable once NonReadonlyMemberInGetHashCode
+            return Id.GetHashCode();
+        }
+    }
+
+    /// <summary>
+    /// A enum is a clone of Java class User with the same namespace.
+    /// </summary>
+    public enum ACL
+    {
+        Allow, Deny
+    }
+
+    /// <summary>
+    /// A class is a clone of Java class Role with the same namespace.
+    /// </summary>
+    public class Role
+    {
+        public String Name { get; set; }
+    }
+
+    /// <summary>
+    /// A class is a clone of Java class User with the same namespace.
+    /// </summary>
+    public class User
+    {
+        public int Id { get; set; }
+        
+        public ACL Acl { get; set; }
+        
+        public Role Role { get; set; }
+    }    
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs
index 53bfbbcc25f3e..82e099323721f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs
@@ -283,32 +283,41 @@ private object InvokeProxyMethod(MethodBase method, object[] args)
                 // 1) Write to a stream
                 inStream.WriteBool(SrvKeepBinary);  // WriteProxyMethod does not do this, but Java does
 
-                ServiceProxySerializer.WriteProxyMethod(_marsh.StartMarshal(inStream), method.Name, 
-                    method, args, PlatformType.DotNet);
+                Marshaller.RegisterSameJavaType.Value = true;
 
-                inStream.SynchronizeOutput();
+                try
+                {
+                    ServiceProxySerializer.WriteProxyMethod(_marsh.StartMarshal(inStream), method.Name,
+                        method, args, PlatformType.DotNet);
 
-                inStream.Seek(0, SeekOrigin.Begin);
+                    inStream.SynchronizeOutput();
 
-                // 2) call InvokeServiceMethod
-                string mthdName;
-                object[] mthdArgs;
+                    inStream.Seek(0, SeekOrigin.Begin);
 
-                ServiceProxySerializer.ReadProxyMethod(inStream, _marsh, out mthdName, out mthdArgs);
+                    // 2) call InvokeServiceMethod
+                    string mthdName;
+                    object[] mthdArgs;
 
-                var result = ServiceProxyInvoker.InvokeServiceMethod(_svc, mthdName, mthdArgs);
+                    ServiceProxySerializer.ReadProxyMethod(inStream, _marsh, out mthdName, out mthdArgs);
 
-                ServiceProxySerializer.WriteInvocationResult(outStream, _marsh, result.Key, result.Value);
+                    var result = ServiceProxyInvoker.InvokeServiceMethod(_svc, mthdName, mthdArgs);
 
-                var writer = _marsh.StartMarshal(outStream);
-                writer.WriteString("unused");  // fake Java exception details
-                writer.WriteString("unused");  // fake Java exception details
+                    ServiceProxySerializer.WriteInvocationResult(outStream, _marsh, result.Key, result.Value);
 
-                outStream.SynchronizeOutput();
+                    var writer = _marsh.StartMarshal(outStream);
+                    writer.WriteString("unused"); // fake Java exception details
+                    writer.WriteString("unused"); // fake Java exception details
 
-                outStream.Seek(0, SeekOrigin.Begin);
+                    outStream.SynchronizeOutput();
 
-                return ServiceProxySerializer.ReadInvocationResult(outStream, _marsh, KeepBinary);
+                    outStream.Seek(0, SeekOrigin.Begin);
+
+                    return ServiceProxySerializer.ReadInvocationResult(outStream, _marsh, KeepBinary);
+                }
+                finally 
+                {
+                    Marshaller.RegisterSameJavaType.Value = true;
+                }
             }
         }
 
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs
new file mode 100644
index 0000000000000..5b78a63f3bc96
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceTypeAutoResolveTest.cs
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Tests.Services
+{
+    using System;
+    using System.Collections;
+    using System.Collections.Generic;
+    using System.IO;
+    using System.Linq;
+    using NUnit.Framework;
+    using org.apache.ignite.platform.model;
+
+    /// <summary>
+    /// Tests checks ability to execute service method without explicit registration of parameter type.
+    /// </summary>
+    public class ServicesTypeAutoResolveTest
+    {
+        /** */
+        private IIgnite _grid1;
+
+        [TestFixtureTearDown]
+        public void FixtureTearDown()
+        {
+            StopGrids();
+        }
+
+        /// <summary>
+        /// Executes before each test.
+        /// </summary>
+        [SetUp]
+        public void SetUp()
+        {
+            StartGrids();
+        }
+
+        /// <summary>
+        /// Executes after each test.
+        /// </summary>
+        [TearDown]
+        public void TearDown()
+        {
+            try
+            {
+                _grid1.GetServices();
+
+                TestUtils.AssertHandleRegistryIsEmpty(1000, _grid1);
+            }
+            catch (Exception)
+            {
+                // Restart grids to cleanup
+                StopGrids();
+
+                throw;
+            }
+            finally
+            {
+                if (TestContext.CurrentContext.Test.Name.StartsWith("TestEventTypes"))
+                    StopGrids(); // clean events for other tests
+            }
+        }
+
+        /// <summary>
+        /// Tests Java service invocation with dynamic proxy.
+        /// Types should be resolved implicitly.
+        /// </summary>
+        [Test]
+        public void TestCallJavaServiceDynamicProxy() 
+        {
+            // Deploy Java service
+            var javaSvcName = TestUtils.DeployJavaService(_grid1);
+            var svc = _grid1.GetServices().GetDynamicServiceProxy(javaSvcName, true);
+
+            doTestService(new JavaServiceDynamicProxy(svc));
+        }
+
+        /// <summary>
+        /// Tests Java service invocation.
+        /// Types should be resolved implicitly.
+        /// </summary>
+        [Test]
+        public void TestCallJavaService()
+        {
+            // Deploy Java service
+            var javaSvcName = TestUtils.DeployJavaService(_grid1);
+
+            var svc = _grid1.GetServices().GetServiceProxy<IJavaService>(javaSvcName, false);
+
+            doTestService(svc);
+
+            Assert.IsNull(svc.testDepartments(null));
+
+            var arr  = new[] {"HR", "IT"}.Select(x => new Department() {Name = x}).ToArray();
+
+            ICollection deps = svc.testDepartments(arr);
+
+            Assert.NotNull(deps);
+            Assert.AreEqual(1, deps.Count);
+            Assert.AreEqual("Executive", deps.OfType<Department>().Select(d => d.Name).ToArray()[0]);
+
+            _grid1.GetServices().Cancel(javaSvcName);
+        }
+
+        /// <summary>
+        /// Tests java service instance.
+        /// </summary>
+        private void doTestService(IJavaService svc)
+        {
+            Assert.IsNull(svc.testAddress(null));
+
+            Address addr = svc.testAddress(new Address {Zip = "000", Addr = "Moscow"});
+
+            Assert.AreEqual("127000", addr.Zip);
+            Assert.AreEqual("Moscow Akademika Koroleva 12", addr.Addr);
+
+            Employee[] emps = new[]
+            {
+                new Employee {Fio = "Sarah Connor", Salary = 1},
+                new Employee {Fio = "John Connor", Salary = 2}
+            };
+
+            Assert.AreEqual(42, svc.testOverload(2, emps));
+            Assert.AreEqual(3, svc.testOverload(1, 2));
+            Assert.AreEqual(5, svc.testOverload(3, 2));
+
+            Assert.IsNull(svc.testEmployees(null));
+
+            emps = svc.testEmployees(emps);
+
+            Assert.NotNull(emps);
+            Assert.AreEqual(1, emps.Length);
+
+            Assert.AreEqual("Kyle Reese", emps[0].Fio);
+            Assert.AreEqual(3, emps[0].Salary);
+
+            Assert.IsNull(svc.testMap(null));
+
+            var map = new Dictionary<Key, Value>();
+
+            map.Add(new Key() {Id = 1}, new Value() {Val = "value1"});
+            map.Add(new Key() {Id = 2}, new Value() {Val = "value2"});
+
+            var res = svc.testMap(map);
+
+            Assert.NotNull(res);
+            Assert.AreEqual(1, res.Count);
+            Assert.AreEqual("value3", ((Value)res[new Key() {Id = 3}]).Val);
+
+            var accs = svc.testAccounts();
+
+            Assert.NotNull(accs);
+            Assert.AreEqual(2, accs.Length);
+            Assert.AreEqual("123", accs[0].Id);
+            Assert.AreEqual("321", accs[1].Id);
+            Assert.AreEqual(42, accs[0].Amount);
+            Assert.AreEqual(0, accs[1].Amount);
+
+            var users = svc.testUsers();
+
+            Assert.NotNull(users);
+            Assert.AreEqual(2, users.Length);
+            Assert.AreEqual(1, users[0].Id);
+            Assert.AreEqual(ACL.Allow, users[0].Acl);
+            Assert.AreEqual("admin", users[0].Role.Name);
+            Assert.AreEqual(2, users[1].Id);
+            Assert.AreEqual(ACL.Deny, users[1].Acl);
+            Assert.AreEqual("user", users[1].Role.Name);
+        }
+
+        /// <summary>
+        /// Starts the grids.
+        /// </summary>
+        private void StartGrids()
+        {
+            if (_grid1 != null)
+                return;
+
+            var path = Path.Combine("Config", "Compute", "compute-grid");
+            _grid1 = Ignition.Start(GetConfiguration(path + "1.xml"));
+        }
+
+        /// <summary>
+        /// Stops the grids.
+        /// </summary>
+        private void StopGrids()
+        {
+            _grid1 = null;
+
+            Ignition.StopAll(true);
+        }
+
+        /// <summary>
+        /// Gets the Ignite configuration.
+        /// </summary>
+        private IgniteConfiguration GetConfiguration(string springConfigUrl)
+        {
+            springConfigUrl = ReplaceFooterSetting(springConfigUrl);
+
+            return new IgniteConfiguration(TestUtils.GetTestConfiguration())
+            {
+                SpringConfigUrl = springConfigUrl
+            };
+        }
+        
+        /// <summary>
+        /// Replaces the footer setting.
+        /// </summary>
+        internal static string ReplaceFooterSetting(string path)
+        {
+            var text = File.ReadAllText(path).Replace(
+                "property name=\"compactFooter\" value=\"true\"",
+                "property name=\"compactFooter\" value=\"false\"");
+
+            path += "_fullFooter";
+
+            File.WriteAllText(path, text);
+
+            Assert.IsTrue(File.Exists(path));
+
+            return path;
+        }
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
index aa8f8ea2c9df7..e978dc7275856 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
@@ -18,9 +18,7 @@
 namespace Apache.Ignite.Core.Tests.Services
 {
     using System;
-    using System.Collections;
     using System.Collections.Generic;
-    using System.Diagnostics.CodeAnalysis;
     using System.IO;
     using System.Linq;
     using System.Runtime.Serialization.Formatters.Binary;
@@ -29,6 +27,7 @@ namespace Apache.Ignite.Core.Tests.Services
     using Apache.Ignite.Core.Cluster;
     using Apache.Ignite.Core.Common;
     using Apache.Ignite.Core.Impl;
+    using Apache.Ignite.Core.Impl.Binary;
     using Apache.Ignite.Core.Resource;
     using Apache.Ignite.Core.Services;
     using NUnit.Framework;
@@ -959,6 +958,45 @@ public void TestCallJavaService()
             Assert.AreEqual(new[] {11, 12, 13}, binSvc.testBinaryObjectArray(binArr)
                 .Select(x => x.GetField<int>("Field")));
 
+            DateTime dt1 = new DateTime(1982, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc);
+            DateTime dt2 = new DateTime(1991, 10, 1, 0, 0, 0, 0, DateTimeKind.Utc);
+
+            Assert.AreEqual(dt2, svc.testDate(dt1));
+
+            svc.testDateArray(new DateTime?[] {dt1, dt2});
+
+            var cache = Grid1.GetOrCreateCache<int, DateTime>("net-dates");
+
+            cache.Put(1, dt1);
+            cache.Put(2, dt2);
+
+            svc.testUTCDateFromCache();
+
+            Assert.AreEqual(dt1, cache.Get(3));
+            Assert.AreEqual(dt2, cache.Get(4));
+
+#if NETCOREAPP
+            //This Date in Europe/Moscow have offset +4.
+            DateTime dt3 = new DateTime(1982, 4, 1, 1, 0, 0, 0, DateTimeKind.Local);
+            //This Date in Europe/Moscow have offset +3.
+            DateTime dt4 = new DateTime(1982, 3, 31, 22, 0, 0, 0, DateTimeKind.Local);
+
+            cache.Put(5, dt3);
+            cache.Put(6, dt4);
+
+            Assert.AreEqual(dt3.ToUniversalTime(), cache.Get(5).ToUniversalTime());
+            Assert.AreEqual(dt4.ToUniversalTime(), cache.Get(6).ToUniversalTime());
+
+            svc.testLocalDateFromCache();
+
+            Assert.AreEqual(dt3, cache.Get(7).ToLocalTime());
+            Assert.AreEqual(dt4, cache.Get(8).ToLocalTime());
+
+            var now = DateTime.Now;
+            cache.Put(9, now);
+            Assert.AreEqual(now.ToUniversalTime(), cache.Get(9).ToUniversalTime());
+#endif
+
             Services.Cancel(javaSvcName);
         }
 
@@ -1042,6 +1080,21 @@ public void TestCallJavaServiceDynamicProxy()
             Assert.AreEqual(guid, svc.testNullUUID(guid));
             Assert.IsNull(svc.testNullUUID(null));
             Assert.AreEqual(guid, svc.testArray(new Guid?[] { guid })[0]);
+
+            DateTime dt1 = new DateTime(1982, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc);
+            DateTime dt2 = new DateTime(1991, 10, 1, 0, 0, 0, 0, DateTimeKind.Utc);
+
+            Assert.AreEqual(dt2, svc.testDate(dt1));
+
+            var cache = Grid1.GetOrCreateCache<int, DateTime>("net-dates");
+
+            cache.Put(1, dt1);
+            cache.Put(2, dt2);
+
+            svc.testUTCDateFromCache();
+
+            Assert.AreEqual(dt1, cache.Get(3));
+            Assert.AreEqual(dt2, cache.Get(4));
         }
 
         /// <summary>
@@ -1139,7 +1192,11 @@ private IgniteConfiguration GetConfiguration(string springConfigUrl)
                     typeof (PlatformComputeBinarizable),
                     typeof (BinarizableObject))
                 {
-                    NameMapper = BinaryBasicNameMapper.SimpleNameInstance
+                    NameMapper = BinaryBasicNameMapper.SimpleNameInstance,
+                    ForceTimestamp = true
+#if NETCOREAPP
+                    , TimestampConverter = new TimestampConverter()
+#endif
                 }
             };
         }
@@ -1505,148 +1562,6 @@ private class BinarizableObject
             public int Val { get; set; }
         }
 
-        /// <summary>
-        /// Java service proxy interface.
-        /// </summary>
-        [SuppressMessage("ReSharper", "InconsistentNaming")]
-        public interface IJavaService
-        {
-            /** */
-            bool isCancelled();
-
-            /** */
-            bool isInitialized();
-
-            /** */
-            bool isExecuted();
-
-            /** */
-            byte test(byte x);
-
-            /** */
-            short test(short x);
-
-            /** */
-            int test(int x);
-
-            /** */
-            long test(long x);
-
-            /** */
-            float test(float x);
-
-            /** */
-            double test(double x);
-
-            /** */
-            char test(char x);
-
-            /** */
-            string test(string x);
-
-            /** */
-            bool test(bool x);
-
-            /** */
-            DateTime test(DateTime x);
-
-            /** */
-            Guid test(Guid x);
-
-            /** */
-            byte? testWrapper(byte? x);
-
-            /** */
-            short? testWrapper(short? x);
-
-            /** */
-            int? testWrapper(int? x);
-
-            /** */
-            long? testWrapper(long? x);
-
-            /** */
-            float? testWrapper(float? x);
-
-            /** */
-            double? testWrapper(double? x);
-
-            /** */
-            char? testWrapper(char? x);
-
-            /** */
-            bool? testWrapper(bool? x);
-
-            /** */
-            byte[] testArray(byte[] x);
-
-            /** */
-            short[] testArray(short[] x);
-
-            /** */
-            int[] testArray(int[] x);
-
-            /** */
-            long[] testArray(long[] x);
-
-            /** */
-            float[] testArray(float[] x);
-
-            /** */
-            double[] testArray(double[] x);
-
-            /** */
-            char[] testArray(char[] x);
-
-            /** */
-            string[] testArray(string[] x);
-
-            /** */
-            bool[] testArray(bool[] x);
-
-            /** */
-            DateTime?[] testArray(DateTime?[] x);
-
-            /** */
-            Guid?[] testArray(Guid?[] x);
-
-            /** */
-            int test(int x, string y);
-
-            /** */
-            int test(string x, int y);
-
-            /** */
-            int? testNull(int? x);
-
-            /** */
-            DateTime? testNullTimestamp(DateTime? x);
-
-            /** */
-            Guid? testNullUUID(Guid? x);
-
-            /** */
-            int testParams(params object[] args);
-
-            /** */
-            PlatformComputeBinarizable testBinarizable(PlatformComputeBinarizable x);
-
-            /** */
-            object[] testBinarizableArrayOfObjects(object[] x);
-
-            /** */
-            IBinaryObject[] testBinaryObjectArray(IBinaryObject[] x);
-
-            /** */
-            PlatformComputeBinarizable[] testBinarizableArray(PlatformComputeBinarizable[] x);
-
-            /** */
-            ICollection testBinarizableCollection(ICollection x);
-
-            /** */
-            IBinaryObject testBinaryObject(IBinaryObject x);
-        }
-
         /// <summary>
         /// Interop class.
         /// </summary>
@@ -1664,5 +1579,28 @@ public class PlatformComputeBinarizable2
             /** */
             public int Field { get; set; }
         }
+        
+#if NETCOREAPP
+        /// <summary>
+        /// Adds support of the local dates to the Ignite timestamp serialization.
+        /// </summary>
+        class TimestampConverter : ITimestampConverter
+        {
+            /** <inheritdoc /> */
+            public void ToJavaTicks(DateTime date, out long high, out int low)
+            {
+                if (date.Kind == DateTimeKind.Local)
+                    date = date.ToUniversalTime();
+
+                BinaryUtils.ToJavaDate(date, out high, out low);
+            }
+
+            /** <inheritdoc /> */
+            public DateTime FromJavaTicks(long high, int low)
+            {
+                return new DateTime(BinaryUtils.JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, DateTimeKind.Utc);
+            }
+        }
+#endif
     }
 }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Common.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Common.cs
index f8b14d29d078f..c2fb90da2d369 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Common.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Common.cs
@@ -544,5 +544,22 @@ public static ICollection GetActiveNotificationListeners(this IIgniteClient clie
             var socket = GetPrivateField<ClientSocket>(failoverSocket, "_socket");
             return GetPrivateField<ICollection>(socket, "_notificationListeners");
         }
+
+        /// <summary>
+        /// Deploys the Java service.
+        /// </summary>
+        public static string DeployJavaService(IIgnite ignite)
+        {
+            const string serviceName = "javaService";
+
+            ignite.GetCompute()
+                .ExecuteJavaTask<object>("org.apache.ignite.platform.PlatformDeployServiceTask", serviceName);
+
+            var services = ignite.GetServices();
+
+            WaitForCondition(() => services.GetServiceDescriptors().Any(x => x.Name == serviceName), 1000);
+
+            return serviceName;
+        }
     }
 }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
index 96d17d7fff9a2..2a42a70d68f78 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -52,6 +52,7 @@
   <ItemGroup>
     <Compile Include="Binary\BinaryBasicNameMapper.cs" />
     <Compile Include="Binary\TimestampAttribute.cs" />
+    <Compile Include="Binary\ITimestampConverter.cs" />
     <Compile Include="Cache\Configuration\CacheKeyConfiguration.cs" />
     <Compile Include="Cache\Configuration\DataPageEvictionMode.cs" />
     <Compile Include="Cache\Configuration\PlatformCacheConfiguration.cs" />
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs
index 32d2c9d6f5d15..b25a9f58420aa 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs
@@ -40,6 +40,11 @@ public class BinaryConfiguration
         /// </summary>
         public const bool DefaultKeepDeserialized = true;
 
+        /// <summary>
+        /// Default <see cref="ForceTimestamp"/> setting.
+        /// </summary>
+        public const bool DefaultForceTimestamp = false;
+
         /** Footer setting. */
         private bool? _compactFooter;
 
@@ -49,6 +54,7 @@ public class BinaryConfiguration
         public BinaryConfiguration()
         {
             KeepDeserialized = DefaultKeepDeserialized;
+            ForceTimestamp = DefaultForceTimestamp;
         }
 
         /// <summary>
@@ -72,6 +78,8 @@ internal void CopyLocalProperties(BinaryConfiguration cfg)
             IdMapper = cfg.IdMapper;
             NameMapper = cfg.NameMapper;
             KeepDeserialized = cfg.KeepDeserialized;
+            ForceTimestamp = cfg.ForceTimestamp;
+            TimestampConverter = cfg.TimestampConverter;
 
             if (cfg.Serializer != null)
             {
@@ -106,7 +114,7 @@ public BinaryConfiguration(params Type[] binaryTypes)
         public ICollection<BinaryTypeConfiguration> TypeConfigurations { get; set; }
 
         /// <summary>
-        /// Gets or sets a collection of assembly-qualified type names 
+        /// Gets or sets a collection of assembly-qualified type names
         /// (the result of <see cref="Type.AssemblyQualifiedName"/>) for binarizable types.
         /// <para />
         /// Shorthand for creating <see cref="BinaryTypeConfiguration"/>.
@@ -129,6 +137,15 @@ public BinaryConfiguration(params Type[] binaryTypes)
         /// </summary>
         public IBinarySerializer Serializer { get; set; }
 
+        /// <summary>
+        /// Gets or sets a converter between <see cref="DateTime"/> and Java Timestamp.
+        /// Called from <see cref="IBinaryWriter.WriteTimestamp"/>, <see cref="IBinaryWriter.WriteTimestampArray"/>,
+        /// <see cref="IBinaryReader.ReadTimestamp"/>, <see cref="IBinaryReader.ReadTimestampArray"/>.
+        /// <para />
+        /// See also <see cref="ForceTimestamp"/>.
+        /// </summary>
+        public ITimestampConverter TimestampConverter { get; set; }
+
         /// <summary>
         /// Default keep deserialized flag.
         /// </summary>
@@ -137,12 +154,12 @@ public BinaryConfiguration(params Type[] binaryTypes)
 
         /// <summary>
         /// Gets or sets a value indicating whether to write footers in compact form.
-        /// When enabled, Ignite will not write fields metadata when serializing objects, 
+        /// When enabled, Ignite will not write fields metadata when serializing objects,
         /// because internally metadata is distributed inside cluster.
         /// This increases serialization performance.
         /// <para/>
         /// <b>WARNING!</b> This mode should be disabled when already serialized data can be taken from some external
-        /// sources (e.g.cache store which stores data in binary form, data center replication, etc.). 
+        /// sources (e.g.cache store which stores data in binary form, data center replication, etc.).
         /// Otherwise binary objects without any associated metadata could could not be deserialized.
         /// </summary>
         [DefaultValue(DefaultCompactFooter)]
@@ -152,6 +169,21 @@ public bool CompactFooter
             set { _compactFooter = value; }
         }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether all DateTime keys, values and object fields
+        /// should be written as a Timestamp.
+        /// <para />
+        /// Timestamp format is required for values used in SQL and for interoperation with other platforms.
+        /// Only UTC values are supported in Timestamp format. Other values will cause an exception on write, unless <see cref="TimestampConverter"/> is provided.
+        /// <para />
+        /// Normally Ignite serializer uses <see cref="IBinaryWriter.WriteObject{T}"/> for DateTime fields,
+        /// keys and values.
+        /// This attribute changes the behavior to <see cref="IBinaryWriter.WriteTimestamp"/>.
+        /// <para />
+        /// See also <see cref="TimestampAttribute"/>, <see cref="BinaryReflectiveSerializer.ForceTimestamp"/>.
+        /// </summary>
+        public bool ForceTimestamp { get; set; }
+
         /// <summary>
         /// Gets the compact footer internal nullable value.
         /// </summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs
index f9874ba23484d..c97b0e61e0168 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs
@@ -21,21 +21,21 @@ namespace Apache.Ignite.Core.Binary
     using Apache.Ignite.Core.Impl.Binary;
 
     /// <summary>
-    /// Binary serializer which reflectively writes all fields except of ones with 
+    /// Binary serializer which reflectively writes all fields except of ones with
     /// <see cref="System.NonSerializedAttribute"/>.
     /// <para />
-    /// Note that Java platform stores dates as a difference between current time 
-    /// and predefined absolute UTC date. Therefore, this difference is always the 
-    /// same for all time zones. .NET, in contrast, stores dates as a difference 
-    /// between current time and some predefined date relative to the current time 
-    /// zone. It means that this difference will be different as you change time zones. 
-    /// To overcome this discrepancy Ignite always converts .Net date to UTC form 
-    /// before serializing and allows user to decide whether to deserialize them 
-    /// in UTC or local form using <c>ReadTimestamp(..., true/false)</c> methods in 
+    /// Note that Java platform stores dates as a difference between current time
+    /// and predefined absolute UTC date. Therefore, this difference is always the
+    /// same for all time zones. .NET, in contrast, stores dates as a difference
+    /// between current time and some predefined date relative to the current time
+    /// zone. It means that this difference will be different as you change time zones.
+    /// To overcome this discrepancy Ignite always converts .Net date to UTC form
+    /// before serializing and allows user to decide whether to deserialize them
+    /// in UTC or local form using <c>ReadTimestamp(..., true/false)</c> methods in
     /// <see cref="IBinaryReader"/> and <see cref="IBinaryRawReader"/>.
     /// This serializer always read dates in UTC form. It means that if you have
     /// local date in any field/property, it will be implicitly converted to UTC
-    /// form after the first serialization-deserialization cycle. 
+    /// form after the first serialization-deserialization cycle.
     /// </summary>
     public sealed class BinaryReflectiveSerializer : IBinarySerializer
     {
@@ -94,7 +94,7 @@ public bool RawMode
         /// Normally serializer uses <see cref="IBinaryWriter.WriteObject{T}"/> for DateTime fields.
         /// This attribute changes the behavior to <see cref="IBinaryWriter.WriteTimestamp"/>.
         /// <para />
-        /// See also <see cref="TimestampAttribute"/>.
+        /// See also <see cref="TimestampAttribute"/>, <see cref="BinaryConfiguration.ForceTimestamp"/>.
         /// </summary>
         public bool ForceTimestamp
         {
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/ITimestampConverter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/ITimestampConverter.cs
new file mode 100644
index 0000000000000..c7604793b9360
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/ITimestampConverter.cs
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Binary
+{
+    using System;
+
+    /// <summary>
+    /// Converts <see cref="DateTime"/> values to Java Timestamp and back.
+    /// </summary>
+    public interface ITimestampConverter
+    {
+        /// <summary>Converts date to Java ticks.</summary>
+        /// <param name="date">Date</param>
+        /// <param name="high">High part (milliseconds).</param>
+        /// <param name="low">Low part (nanoseconds)</param>
+        void ToJavaTicks(DateTime date, out long high, out int low);
+
+        /// <summary>Converts date from Java ticks.</summary>
+        /// <param name="high">High part (milliseconds).</param>
+        /// <param name="low">Low part (nanoseconds)</param>
+        DateTime FromJavaTicks(long high, int low);
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd
index ba64aca369216..c0314b3488609 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd
@@ -159,6 +159,18 @@
                                     </xs:attribute>
                                 </xs:complexType>
                             </xs:element>
+                            <xs:element name="timestampConverter" minOccurs="0">
+                                <xs:annotation>
+                                    <xs:documentation>Default date time converter.</xs:documentation>
+                                </xs:annotation>
+                                <xs:complexType>
+                                    <xs:attribute name="type" type="xs:string" use="required">
+                                        <xs:annotation>
+                                            <xs:documentation>Assembly-qualified type name.</xs:documentation>
+                                        </xs:annotation>
+                                    </xs:attribute>
+                                </xs:complexType>
+                            </xs:element>
                         </xs:all>
                         <xs:attribute name="keepDeserialized" type="xs:boolean">
                             <xs:annotation>
@@ -170,6 +182,11 @@
                                 <xs:documentation>Compact footer flag.</xs:documentation>
                             </xs:annotation>
                         </xs:attribute>
+                        <xs:attribute name="forceTimestamp" type="xs:boolean">
+                            <xs:annotation>
+                                <xs:documentation>Force timestamp flag.</xs:documentation>
+                            </xs:annotation>
+                        </xs:attribute>
                     </xs:complexType>
                 </xs:element>
                 <xs:element name="endpoints" minOccurs="0" maxOccurs="1">
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
index 0ab59ec78bea8..7794c21ce234e 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
@@ -263,6 +263,18 @@
                                     </xs:attribute>
                                 </xs:complexType>
                             </xs:element>
+                            <xs:element name="timestampConverter" minOccurs="0">
+                                <xs:annotation>
+                                    <xs:documentation>Default date time converter.</xs:documentation>
+                                </xs:annotation>
+                                <xs:complexType>
+                                    <xs:attribute name="type" type="xs:string" use="required">
+                                        <xs:annotation>
+                                            <xs:documentation>Assembly-qualified type name.</xs:documentation>
+                                        </xs:annotation>
+                                    </xs:attribute>
+                                </xs:complexType>
+                            </xs:element>
                         </xs:all>
                         <xs:attribute name="keepDeserialized" type="xs:boolean">
                             <xs:annotation>
@@ -274,6 +286,11 @@
                                 <xs:documentation>Compact footer flag.</xs:documentation>
                             </xs:annotation>
                         </xs:attribute>
+                        <xs:attribute name="forceTimestamp" type="xs:boolean">
+                            <xs:annotation>
+                                <xs:documentation>Force timestamp flag.</xs:documentation>
+                            </xs:annotation>
+                        </xs:attribute>
                     </xs:complexType>
                 </xs:element>
                 <xs:element name="cacheConfiguration" minOccurs="0">
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs
index b62977a53a02a..05deb5ba18b37 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs
@@ -27,6 +27,12 @@ namespace Apache.Ignite.Core.Impl.Binary
     /// </summary>
     internal class BinaryProcessor : PlatformTargetAdapter, IBinaryProcessor
     {
+        /** Java platform id. See org.apache.ignite.internal.MarshallerPlatformIds in Java. */
+        public const byte JavaPlatformId = 0;
+
+        /** Type registry platform id. See org.apache.ignite.internal.MarshallerPlatformIds in Java. */
+        public const byte DotNetPlatformId = 1;
+
         /// <summary>
         /// Op codes.
         /// </summary>
@@ -103,8 +109,9 @@ public void PutBinaryTypes(ICollection<BinaryType> types)
         /// </summary>
         /// <param name="id">The identifier.</param>
         /// <param name="typeName">The type name.</param>
+        /// <param name="registerSameJavaType">True if should register type both for dotnet and java platforms.</param>	
         /// <returns>True if registration succeeded; otherwise, false.</returns>
-        public bool RegisterType(int id, string typeName)
+        public bool RegisterType(int id, string typeName, bool registerSameJavaType)
         {
             Debug.Assert(typeName != null);
             Debug.Assert(id != BinaryTypeId.Unregistered);
@@ -113,6 +120,7 @@ public bool RegisterType(int id, string typeName)
             {
                 w.WriteInt(id);
                 w.WriteString(typeName);
+                w.WriteBoolean(registerSameJavaType);
             }) == True;
         }
 
@@ -160,7 +168,38 @@ public BinaryType RegisterEnum(string typeName, IEnumerable<KeyValuePair<string,
         /// <returns>Type or null.</returns>
         public string GetTypeName(int id)
         {
-            return DoOutInOp((int) Op.GetType, w => w.WriteInt(id), r => Marshaller.StartUnmarshal(r).ReadString());
+            try
+            {
+                return GetTypeName(id, DotNetPlatformId);
+            }
+            catch (BinaryObjectException)
+            {
+                if (!Marshaller.RegisterSameJavaType.Value)
+                    throw;
+            }
+
+            // Try to get java type name and register corresponding DotNet type.
+            var javaTypeName = GetTypeName(id, JavaPlatformId);
+            var netTypeName = Marshaller.GetTypeName(javaTypeName);
+
+            RegisterType(id, netTypeName, false);
+
+            return netTypeName;
+        }
+
+        /// <summary>
+        /// Gets the type name by id for specific platform.
+        /// </summary>
+        /// <param name="id">The identifier.</param>
+        /// <param name="platformId">Platform identifier.</param>
+        /// <returns>Type or null.</returns>
+        private string GetTypeName(int id, byte platformId)
+        {
+            return DoOutInOp((int) Op.GetType, w =>
+            {
+                w.WriteInt(id);
+                w.WriteByte(platformId);
+            }, r => Marshaller.StartUnmarshal(r).ReadString());
         }
 
         /// <summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs
index 8ef84c1fe82f3..4f67c085aab1c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs
@@ -28,9 +28,6 @@ namespace Apache.Ignite.Core.Impl.Binary
     /// </summary>
     internal class BinaryProcessorClient : IBinaryProcessor
     {
-        /** Type registry platform id. See org.apache.ignite.internal.MarshallerPlatformIds in Java. */
-        private const byte DotNetPlatformId = 1;
-
         /** Socket. */
         private readonly ClientFailoverSocket _socket;
 
@@ -73,14 +70,26 @@ public void PutBinaryTypes(ICollection<BinaryType> types)
         }
 
         /** <inheritdoc /> */
-        public bool RegisterType(int id, string typeName)
+        public bool RegisterType(int id, string typeName, bool registerSameJavaType)
         {
-            return _socket.DoOutInOp(ClientOp.BinaryTypeNamePut, ctx =>
+            var res = _socket.DoOutInOp(ClientOp.BinaryTypeNamePut, ctx =>
             {
-                ctx.Stream.WriteByte(DotNetPlatformId);
+                ctx.Stream.WriteByte(BinaryProcessor.DotNetPlatformId);
                 ctx.Stream.WriteInt(id);
                 ctx.Writer.WriteString(typeName);
             }, ctx => ctx.Stream.ReadBool());
+
+            if (registerSameJavaType && res)
+            {
+                res = _socket.DoOutInOp(ClientOp.BinaryTypeNamePut, ctx =>
+                {
+                    ctx.Stream.WriteByte(BinaryProcessor.JavaPlatformId);
+                    ctx.Stream.WriteInt(id);
+                    ctx.Writer.WriteString(typeName);
+                }, ctx => ctx.Stream.ReadBool());
+            }
+
+            return res;
         }
 
         /** <inheritdoc /> */
@@ -94,7 +103,7 @@ public string GetTypeName(int id)
         {
             return _socket.DoOutInOp(ClientOp.BinaryTypeNameGet, ctx =>
                 {
-                    ctx.Stream.WriteByte(DotNetPlatformId);
+                    ctx.Stream.WriteByte(BinaryProcessor.DotNetPlatformId);
                     ctx.Stream.WriteInt(id);
                 },
                 ctx => ctx.Reader.ReadString());
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs
index 9153046ece52b..c1a6fed02ddd2 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs
@@ -315,25 +315,25 @@ public double[] ReadDoubleArray()
         /** <inheritdoc /> */
         public DateTime? ReadTimestamp(string fieldName)
         {
-            return ReadField(fieldName, BinaryUtils.ReadTimestamp, BinaryTypeId.Timestamp);
+            return ReadField(fieldName, stream => BinaryUtils.ReadTimestamp(stream, _marsh.TimestampConverter), BinaryTypeId.Timestamp);
         }
 
         /** <inheritdoc /> */
         public DateTime? ReadTimestamp()
         {
-            return Read(BinaryUtils.ReadTimestamp, BinaryTypeId.Timestamp);
+            return Read(stream => BinaryUtils.ReadTimestamp(stream, _marsh.TimestampConverter), BinaryTypeId.Timestamp);
         }
         
         /** <inheritdoc /> */
         public DateTime?[] ReadTimestampArray(string fieldName)
         {
-            return ReadField(fieldName, BinaryUtils.ReadTimestampArray, BinaryTypeId.ArrayTimestamp);
+            return ReadField(fieldName, stream => BinaryUtils.ReadTimestampArray(stream, _marsh.TimestampConverter), BinaryTypeId.ArrayTimestamp);
         }
         
         /** <inheritdoc /> */
         public DateTime?[] ReadTimestampArray()
         {
-            return Read(BinaryUtils.ReadTimestampArray, BinaryTypeId.ArrayTimestamp);
+            return Read(stream => BinaryUtils.ReadTimestampArray(stream, _marsh.TimestampConverter), BinaryTypeId.ArrayTimestamp);
         }
         
         /** <inheritdoc /> */
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs
index cae0f5fcf9a50..8fdf3716fe623 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs
@@ -54,9 +54,6 @@ static BinarySystemHandlers()
             ReadHandlers[BinaryTypeId.Double] = new BinarySystemReader<double>(s => s.ReadDouble());
             ReadHandlers[BinaryTypeId.Decimal] = new BinarySystemReader<decimal?>(BinaryUtils.ReadDecimal);
 
-            // 2. Date.
-            ReadHandlers[BinaryTypeId.Timestamp] = new BinarySystemReader<DateTime?>(BinaryUtils.ReadTimestamp);
-
             // 3. String.
             ReadHandlers[BinaryTypeId.String] = new BinarySystemReader<string>(BinaryUtils.ReadString);
 
@@ -92,10 +89,6 @@ static BinarySystemHandlers()
             ReadHandlers[BinaryTypeId.ArrayDecimal] =
                 new BinarySystemReader<decimal?[]>(BinaryUtils.ReadDecimalArray);
 
-            // 6. Date array.
-            ReadHandlers[BinaryTypeId.ArrayTimestamp] =
-                new BinarySystemReader<DateTime?[]>(BinaryUtils.ReadTimestampArray);
-
             // 7. String array.
             ReadHandlers[BinaryTypeId.ArrayString] = new BinarySystemTypedArrayReader<string>();
 
@@ -122,9 +115,18 @@ static BinarySystemHandlers()
         /// Try getting write handler for type.
         /// </summary>
         /// <param name="type"></param>
+        /// <param name="forceTimestamp"></param>
         /// <returns></returns>
-        public static IBinarySystemWriteHandler GetWriteHandler(Type type)
+        public static IBinarySystemWriteHandler GetWriteHandler(Type type, bool forceTimestamp)
         {
+            if (forceTimestamp)
+            {
+                if (type == typeof(DateTime))
+                    return new BinarySystemWriteHandler<DateTime>(WriteTimestamp, false);
+                if (type == typeof(DateTime?[]))
+                    return new BinarySystemWriteHandler<DateTime?[]>(WriteTimestampArray, true);
+            }
+
             return WriteHandlers.GetOrAdd(type, t =>
             {
                 return FindWriteHandler(t);
@@ -249,6 +251,20 @@ public static bool TryReadSystemType<T>(byte typeId, BinaryReader ctx, out T res
 
             if (handler == null)
             {
+                if (typeId == BinaryTypeId.Timestamp)
+                {
+                    // Date.
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadTimestamp(ctx.Stream, ctx.Marshaller.TimestampConverter));
+                    return true;
+                }
+
+                if (typeId == BinaryTypeId.ArrayTimestamp)
+                {
+                    // Date array.
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadTimestampArray(ctx.Stream, ctx.Marshaller.TimestampConverter));
+                    return true;
+                }
+
                 res = default(T);
                 return false;
             }
@@ -293,6 +309,18 @@ private static void WriteGuid(BinaryWriter ctx, Guid obj)
             BinaryUtils.WriteGuid(obj, ctx.Stream);
         }
 
+        /// <summary>
+        /// Write Date.
+        /// </summary>
+        /// <param name="ctx">Context.</param>
+        /// <param name="obj">Value.</param>
+        private static void WriteTimestamp(BinaryWriter ctx, DateTime obj)
+        {
+            ctx.Stream.WriteByte(BinaryTypeId.Timestamp);
+
+            BinaryUtils.WriteTimestamp(obj, ctx.Stream, ctx.Marshaller.TimestampConverter);
+        }
+
         /// <summary>
         /// Write boolaen array.
         /// </summary>
@@ -425,6 +453,18 @@ private static void WriteGuidArray(BinaryWriter ctx, Guid?[] obj)
             BinaryUtils.WriteGuidArray(obj, ctx.Stream);
         }
 
+        /// <summary>
+        /// Write nullable Date array.
+        /// </summary>
+        /// <param name="ctx">Context.</param>
+        /// <param name="obj">Value.</param>
+        private static void WriteTimestampArray(BinaryWriter ctx, DateTime?[] obj)
+        {
+            ctx.Stream.WriteByte(BinaryTypeId.ArrayTimestamp);
+
+            BinaryUtils.WriteTimestampArray(obj, ctx.Stream, ctx.Marshaller.TimestampConverter);
+        }
+
         /// <summary>
         /// Writes the enum array.
         /// </summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs
index 445d42162f4a8..7a1eb9ecb99b3 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs
@@ -69,7 +69,7 @@ internal static class BinaryUtils
         public const int ObjTypeId = -1;
 
         /** Ticks for Java epoch. */
-        private static readonly long JavaDateTicks = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).Ticks;
+        public static readonly long JavaDateTicks = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).Ticks;
 
         /** Binding flags for static search. */
         private const BindingFlags BindFlagsStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
@@ -384,13 +384,17 @@ public static double[] ReadDoubleArray(IBinaryStream stream)
          * <summary>Write date.</summary>
          * <param name="val">Date.</param>
          * <param name="stream">Stream.</param>
+         * <param name="converter">Timestamp Converter.</param>
          */
-        public static void WriteTimestamp(DateTime val, IBinaryStream stream)
+        public static void WriteTimestamp(DateTime val, IBinaryStream stream, ITimestampConverter converter)
         {
             long high;
             int low;
 
-            ToJavaDate(val, out high, out low);
+            if (converter != null)
+                converter.ToJavaTicks(val, out high, out low);
+            else
+                ToJavaDate(val, out high, out low);
 
             stream.WriteLong(high);
             stream.WriteInt(low);
@@ -399,14 +403,18 @@ public static void WriteTimestamp(DateTime val, IBinaryStream stream)
         /**
          * <summary>Read date.</summary>
          * <param name="stream">Stream.</param>
+         * <param name="converter">Timestamp Converter.</param>
          * <returns>Date</returns>
          */
-        public static DateTime? ReadTimestamp(IBinaryStream stream)
+        public static DateTime? ReadTimestamp(IBinaryStream stream, ITimestampConverter converter)
         {
             long high = stream.ReadLong();
             int low = stream.ReadInt();
 
-            return new DateTime(JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, DateTimeKind.Utc);
+            if (converter != null)
+                return converter.FromJavaTicks(high, low);
+            else
+                return new DateTime(JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, DateTimeKind.Utc);
         }
 
         /// <summary>
@@ -434,7 +442,8 @@ public static long DateTimeToJavaTicks(DateTime dateTime)
         /// </summary>
         /// <param name="vals">Values.</param>
         /// <param name="stream">Stream.</param>
-        public static void WriteTimestampArray(DateTime?[] vals, IBinaryStream stream)
+        /// <param name="converter">Timestamp Converter.</param>
+        public static void WriteTimestampArray(DateTime?[] vals, IBinaryStream stream, ITimestampConverter converter)
         {
             stream.WriteInt(vals.Length);
 
@@ -444,7 +453,7 @@ public static void WriteTimestampArray(DateTime?[] vals, IBinaryStream stream)
                 {
                     stream.WriteByte(BinaryTypeId.Timestamp);
 
-                    WriteTimestamp(val.Value, stream);
+                    WriteTimestamp(val.Value, stream, converter);
                 }
                 else
                     stream.WriteByte(HdrNull);
@@ -1161,15 +1170,16 @@ public static T[] ReadArray<T>(BinaryReader ctx, bool typed)
         /// Read timestamp array.
         /// </summary>
         /// <param name="stream">Stream.</param>
+        /// <param name="converter">Timestamp Converter.</param>
         /// <returns>Timestamp array.</returns>
-        public static DateTime?[] ReadTimestampArray(IBinaryStream stream)
+        public static DateTime?[] ReadTimestampArray(IBinaryStream stream, ITimestampConverter converter)
         {
             int len = stream.ReadInt();
 
             DateTime?[] vals = new DateTime?[len];
 
             for (int i = 0; i < len; i++)
-                vals[i] = stream.ReadByte() == HdrNull ? null : ReadTimestamp(stream);
+                vals[i] = stream.ReadByte() == HdrNull ? null : ReadTimestamp(stream, converter);
 
             return vals;
         }
@@ -1608,7 +1618,7 @@ public static void ValidateProtocolVersion(byte version)
          * <param name="high">High part (milliseconds).</param>
          * <param name="low">Low part (nanoseconds)</param>
          */
-        private static void ToJavaDate(DateTime date, out long high, out int low)
+        public static void ToJavaDate(DateTime date, out long high, out int low)
         {
             if (date.Kind != DateTimeKind.Utc)
             {
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
index 4abac0a63445c..3054bc0554aab 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
@@ -657,7 +657,7 @@ public void WriteTimestamp(string fieldName, DateTime? val)
             else
             {
                 _stream.WriteByte(BinaryTypeId.Timestamp);
-                BinaryUtils.WriteTimestamp(val.Value, _stream);
+                BinaryUtils.WriteTimestamp(val.Value, _stream, _marsh.TimestampConverter);
             }
         }
         
@@ -672,7 +672,7 @@ public void WriteTimestamp(DateTime? val)
             else
             {
                 _stream.WriteByte(BinaryTypeId.Timestamp);
-                BinaryUtils.WriteTimestamp(val.Value, _stream);
+                BinaryUtils.WriteTimestamp(val.Value, _stream, _marsh.TimestampConverter);
             }
         }
 
@@ -690,7 +690,7 @@ public void WriteTimestampArray(string fieldName, DateTime?[] val)
             else
             {
                 _stream.WriteByte(BinaryTypeId.ArrayTimestamp);
-                BinaryUtils.WriteTimestampArray(val, _stream);
+                BinaryUtils.WriteTimestampArray(val, _stream, _marsh.TimestampConverter);
             }
         }
 
@@ -705,7 +705,7 @@ public void WriteTimestampArray(DateTime?[] val)
             else
             {
                 _stream.WriteByte(BinaryTypeId.ArrayTimestamp);
-                BinaryUtils.WriteTimestampArray(val, _stream);
+                BinaryUtils.WriteTimestampArray(val, _stream, _marsh.TimestampConverter);
             }
         }
 
@@ -875,7 +875,7 @@ public void WriteEnum<T>(T val)
                     throw new BinaryObjectException("Type is not an enum: " + type);
                 }
 
-                var handler = BinarySystemHandlers.GetWriteHandler(type);
+                var handler = BinarySystemHandlers.GetWriteHandler(type, _marsh.ForceTimestamp);
 
                 if (handler != null)
                 {
@@ -1163,7 +1163,7 @@ public void Write<T>(T obj)
                 return;
 
             // Are we dealing with a well-known type?
-            var handler = BinarySystemHandlers.GetWriteHandler(type);
+            var handler = BinarySystemHandlers.GetWriteHandler(type, _marsh.ForceTimestamp);
 
             if (handler != null)
             {
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryProcessor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryProcessor.cs
index fed78c3f4eb9f..6dfefb1354b95 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryProcessor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryProcessor.cs
@@ -47,8 +47,9 @@ internal interface IBinaryProcessor
         /// </summary>
         /// <param name="id">The identifier.</param>
         /// <param name="typeName">The type name.</param>
+        /// <param name="registerSameJavaType">True if should register type both for dotnet and java platforms.</param>	
         /// <returns>True if registration succeeded; otherwise, false.</returns>
-        bool RegisterType(int id, string typeName);
+        bool RegisterType(int id, string typeName, bool registerSameJavaType);
 
         /// <summary>
         /// Registers the enum.
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs
index 056a713451f3f..d09efbed5fcce 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs
@@ -22,6 +22,7 @@ namespace Apache.Ignite.Core.Impl.Binary
     using System.Diagnostics;
     using System.Linq;
     using System.Runtime.Serialization;
+    using System.Threading;
     using Apache.Ignite.Core.Binary;
     using Apache.Ignite.Core.Cache.Affinity;
     using Apache.Ignite.Core.Common;
@@ -42,6 +43,9 @@ namespace Apache.Ignite.Core.Impl.Binary
     /// </summary>
     internal class Marshaller
     {
+        /** Register same java type flag. */
+        public static readonly ThreadLocal<Boolean> RegisterSameJavaType = new ThreadLocal<Boolean>(() => false);
+
         /** Binary configuration. */
         private readonly BinaryConfiguration _cfg;
 
@@ -132,6 +136,22 @@ public IIgniteInternal Ignite
         /// </summary>
         public bool RegistrationDisabled { get; set; }
 
+        /// <summary>
+        /// Gets force timestamp flag value.
+        /// </summary>
+        public bool ForceTimestamp
+        {
+            get { return _cfg.ForceTimestamp; }
+        }
+
+        /// <summary>
+        /// Gets date time converter.
+        /// </summary>
+        public ITimestampConverter TimestampConverter
+        {
+            get { return _cfg.TimestampConverter; }
+        }
+
         /// <summary>
         /// Marshal object.
         /// </summary>
@@ -372,7 +392,7 @@ private BinaryTypeHolder GetBinaryTypeHolder(IBinaryTypeDescriptor desc)
             {
                 return holder;
             }
-            
+
             lock (this)
             {
                 if (!_metas.TryGetValue(desc.TypeId, out holder))
@@ -390,9 +410,9 @@ private BinaryTypeHolder GetBinaryTypeHolder(IBinaryTypeDescriptor desc)
 
             return holder;
         }
-        
+
         /// <summary>
-        /// Updates or creates cached binary type holder. 
+        /// Updates or creates cached binary type holder.
         /// </summary>
         private void UpdateOrCreateBinaryTypeHolder(BinaryType meta)
         {
@@ -402,7 +422,7 @@ private void UpdateOrCreateBinaryTypeHolder(BinaryType meta)
                 holder.Merge(meta);
                 return;
             }
-            
+
             lock (this)
             {
                 if (_metas.TryGetValue(meta.TypeId, out holder))
@@ -410,7 +430,7 @@ private void UpdateOrCreateBinaryTypeHolder(BinaryType meta)
                     holder.Merge(meta);
                     return;
                 }
-                
+
                 var metas0 = new Dictionary<int, BinaryTypeHolder>(_metas);
 
                 holder = new BinaryTypeHolder(meta.TypeId, meta.TypeName, meta.AffinityKeyFieldName, meta.IsEnum, this);
@@ -556,7 +576,7 @@ private BinaryFullTypeDescriptor RegisterType(Type type, BinaryFullTypeDescripto
             var typeName = GetTypeName(type);
             var typeId = GetTypeId(typeName, _cfg.IdMapper);
 
-            var registered = _ignite != null && _ignite.BinaryProcessor.RegisterType(typeId, typeName);
+            var registered = _ignite != null && _ignite.BinaryProcessor.RegisterType(typeId, typeName, RegisterSameJavaType.Value);
 
             return AddUserType(type, typeId, typeName, registered, desc);
         }
@@ -692,7 +712,10 @@ private static IBinarySerializerInternal GetSerializer(BinaryConfiguration cfg,
                     return new SerializableSerializer(type);
                 }
 
-                serializer = new BinaryReflectiveSerializer();
+                serializer = new BinaryReflectiveSerializer
+                {
+                    ForceTimestamp = cfg != null && cfg.ForceTimestamp
+                };
             }
 
             var refSerializer = serializer as BinaryReflectiveSerializer;
@@ -867,16 +890,16 @@ public void OnClientReconnected(bool clusterRestarted)
         {
             if (!clusterRestarted)
                 return;
-            
+
             // Reset all binary structures. Metadata must be sent again.
             // _idToDesc enumerator is thread-safe (returns a snapshot).
             // If there are new descriptors added concurrently, they are fine (we are already connected).
-            
+
             // Race is possible when serialization is started before reconnect (or even before disconnect)
             // and finished after reconnect, meta won't be sent to cluster because it is assumed to be known,
             // but operation will succeed.
             // We don't support this use case. Users should handle reconnect events properly when cluster is restarted.
-            // Supporting this very rare use case will complicate the code a lot with little benefit. 
+            // Supporting this very rare use case will complicate the code a lot with little benefit.
             foreach (var desc in _idToDesc)
             {
                 desc.Value.ResetWriteStructure();
@@ -886,7 +909,7 @@ public void OnClientReconnected(bool clusterRestarted)
         /// <summary>
         /// Gets the name of the type.
         /// </summary>
-        private string GetTypeName(string fullTypeName, IBinaryNameMapper mapper = null)
+        public string GetTypeName(string fullTypeName, IBinaryNameMapper mapper = null)
         {
             mapper = mapper ?? _cfg.NameMapper ?? GetDefaultNameMapper();
 
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs
index 37453705d145f..e28f4d78ccc65 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxySerializer.cs
@@ -69,7 +69,8 @@ public static void WriteProxyMethod(BinaryWriter writer, string methodName, Meth
 
                     for (var i = 0; i < arguments.Length; i++)
                     {
-                        WriteArgForPlatforms(writer, mParams != null ? mParams[i].ParameterType : null, arguments[i]);
+                        WriteArgForPlatforms(writer, mParams != null ? mParams[i].ParameterType : null,
+                            arguments[i]);
                     }
                 }
             }
@@ -257,7 +258,7 @@ private static Action<BinaryWriter, object> GetPlatformArgWriter(Type paramType,
                     return (writer, o) => writer.WriteTimestampArray((DateTime?[]) o);
             }
 
-            var handler = BinarySystemHandlers.GetWriteHandler(type);
+            var handler = BinarySystemHandlers.GetWriteHandler(type, true);
 
             if (handler != null)
                 return null;
@@ -265,12 +266,12 @@ private static Action<BinaryWriter, object> GetPlatformArgWriter(Type paramType,
             if (type.IsArray)
                 return (writer, o) => writer.WriteArrayInternal((Array) o);
 
+            if (arg is IDictionary)
+                return (writer, o) => writer.WriteDictionary((IDictionary) o);
+
             if (arg is ICollection)
                 return (writer, o) => writer.WriteCollection((ICollection) o);
 
-            if (arg is DateTime)
-                return (writer, o) => writer.WriteTimestamp((DateTime) o);
-
             return null;
         }
     }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs
index 204d3aeeee78c..3afe34bfc8060 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs
@@ -434,10 +434,20 @@ public dynamic GetDynamicServiceProxy(string name, bool sticky)
         private object InvokeProxyMethod(IPlatformTargetInternal proxy, string methodName,
             MethodBase method, object[] args, PlatformType platformType)
         {
-            return DoOutInOp(OpInvokeMethod,
-                writer => ServiceProxySerializer.WriteProxyMethod(writer, methodName, method, args, platformType),
-                (stream, res) => ServiceProxySerializer.ReadInvocationResult(stream, Marshaller, _keepBinary), 
-                proxy);
+            Marshaller.RegisterSameJavaType.Value = true;
+
+            try
+            {
+                return DoOutInOp(OpInvokeMethod,
+                    writer => ServiceProxySerializer.WriteProxyMethod(writer, methodName, method, args, platformType),
+                    (stream, res) => ServiceProxySerializer.ReadInvocationResult(stream, Marshaller, _keepBinary),
+                    proxy);
+            }
+            finally
+            {
+                Marshaller.RegisterSameJavaType.Value = false;
+            }
+
         }
 
         /// <summary>