diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000000..bdaa5ba9827
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,50 @@
+*.doc diff=astextplain
+*.DOC diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot diff=astextplain
+*.DOT diff=astextplain
+*.pdf diff=astextplain
+*.PDF diff=astextplain
+*.rtf diff=astextplain
+*.RTF diff=astextplain
+
+*.jpg binary
+*.png binary
+*.gif binary
+
+*.cs text=auto diff=csharp
+*.vb text=auto
+*.resx text=auto
+*.c text=auto
+*.cpp text=auto
+*.cxx text=auto
+*.h text=auto
+*.hxx text=auto
+*.py text=auto
+*.rb text=auto
+*.java text=auto
+*.html text=auto
+*.htm text=auto
+*.css text=auto
+*.scss text=auto
+*.sass text=auto
+*.less text=auto
+*.js text=auto
+*.lisp text=auto
+*.clj text=auto
+*.sql text=auto
+*.php text=auto
+*.lua text=auto
+*.m text=auto
+*.asm text=auto
+*.erl text=auto
+*.fs text=auto
+*.fsx text=auto
+*.hs text=auto
+
+*.csproj text=auto
+*.vbproj text=auto
+*.fsproj text=auto
+*.dbproj text=auto
+*.sln text=auto eol=crlf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000000..5cf7c13c7e3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+[Oo]bj/
+[Bb]in/
+*.xap
+*.user
+/TestResults
+*.vspscc
+*.vssscc
+*.suo
+*.cache
+*.docstates
+_ReSharper.*
+*.csproj.user
+*[Rr]e[Ss]harper.user
+_ReSharper.*/
+packages/*
+artifacts/*
+msbuild.log
+PublishProfiles/
+*.psess
+*.vsp
+*.pidb
+*.userprefs
+*DS_Store
+*.ncrunchsolution
+*.log
+*.vspx
+/.symbols
+nuget.exe
+build/
+*net45.csproj
+*k10.csproj
\ No newline at end of file
diff --git a/Data.sln b/Data.sln
new file mode 100644
index 00000000000..58004f24e19
--- /dev/null
+++ b/Data.sln
@@ -0,0 +1,117 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.30110.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.Core", "src\Microsoft.Data.Core\Microsoft.Data.Core.csproj", "{6A531BB3-DF57-4743-AF3B-4BE431B67DE6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.InMemory", "src\Microsoft.Data.InMemory\Microsoft.Data.InMemory.csproj", "{01F860AB-80DD-4AB4-B495-D5B20A493E60}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.Core.UnitTest", "test\Microsoft.Data.Tests.Unit\Microsoft.Data.Core.UnitTest.csproj", "{FB272D58-D5FD-41D1-9E02-BCE17FD97F0E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.Core.FunctionalTest", "test\Microsoft.Data.Tests.Functional\Microsoft.Data.Core.FunctionalTest.csproj", "{A08C9073-CB43-409E-9A5E-4BE2CA687FE3}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{35BED7DE-CA6F-41EA-91E3-25CA7295C7B9}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{547020C0-976F-4542-81CE-7FD465595614}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.Relational", "src\Microsoft.Data.Relational\Microsoft.Data.Relational.csproj", "{8C045113-8E76-4910-9C61-95BC82BB3003}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SQLite", "src\Microsoft.Data.SQLite\Microsoft.Data.SQLite.csproj", "{33D71368-9175-4A74-831E-11ACE6D815EE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlServer", "src\Microsoft.Data.SqlServer\Microsoft.Data.SqlServer.csproj", "{D30CDA1E-CD18-42CE-934F-F33162F1B6EC}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|Mixed Platforms = Debug|Mixed Platforms
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|Mixed Platforms = Release|Mixed Platforms
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6}.Release|x86.ActiveCfg = Release|Any CPU
+ {01F860AB-80DD-4AB4-B495-D5B20A493E60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {01F860AB-80DD-4AB4-B495-D5B20A493E60}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {01F860AB-80DD-4AB4-B495-D5B20A493E60}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {01F860AB-80DD-4AB4-B495-D5B20A493E60}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {01F860AB-80DD-4AB4-B495-D5B20A493E60}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {01F860AB-80DD-4AB4-B495-D5B20A493E60}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {01F860AB-80DD-4AB4-B495-D5B20A493E60}.Release|Any CPU.Build.0 = Release|Any CPU
+ {01F860AB-80DD-4AB4-B495-D5B20A493E60}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {01F860AB-80DD-4AB4-B495-D5B20A493E60}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {01F860AB-80DD-4AB4-B495-D5B20A493E60}.Release|x86.ActiveCfg = Release|Any CPU
+ {FB272D58-D5FD-41D1-9E02-BCE17FD97F0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FB272D58-D5FD-41D1-9E02-BCE17FD97F0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FB272D58-D5FD-41D1-9E02-BCE17FD97F0E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {FB272D58-D5FD-41D1-9E02-BCE17FD97F0E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {FB272D58-D5FD-41D1-9E02-BCE17FD97F0E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {FB272D58-D5FD-41D1-9E02-BCE17FD97F0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FB272D58-D5FD-41D1-9E02-BCE17FD97F0E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FB272D58-D5FD-41D1-9E02-BCE17FD97F0E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {FB272D58-D5FD-41D1-9E02-BCE17FD97F0E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {FB272D58-D5FD-41D1-9E02-BCE17FD97F0E}.Release|x86.ActiveCfg = Release|Any CPU
+ {A08C9073-CB43-409E-9A5E-4BE2CA687FE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A08C9073-CB43-409E-9A5E-4BE2CA687FE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A08C9073-CB43-409E-9A5E-4BE2CA687FE3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {A08C9073-CB43-409E-9A5E-4BE2CA687FE3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {A08C9073-CB43-409E-9A5E-4BE2CA687FE3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A08C9073-CB43-409E-9A5E-4BE2CA687FE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A08C9073-CB43-409E-9A5E-4BE2CA687FE3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A08C9073-CB43-409E-9A5E-4BE2CA687FE3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {A08C9073-CB43-409E-9A5E-4BE2CA687FE3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {A08C9073-CB43-409E-9A5E-4BE2CA687FE3}.Release|x86.ActiveCfg = Release|Any CPU
+ {8C045113-8E76-4910-9C61-95BC82BB3003}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8C045113-8E76-4910-9C61-95BC82BB3003}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8C045113-8E76-4910-9C61-95BC82BB3003}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {8C045113-8E76-4910-9C61-95BC82BB3003}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {8C045113-8E76-4910-9C61-95BC82BB3003}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {8C045113-8E76-4910-9C61-95BC82BB3003}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8C045113-8E76-4910-9C61-95BC82BB3003}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8C045113-8E76-4910-9C61-95BC82BB3003}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {8C045113-8E76-4910-9C61-95BC82BB3003}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {8C045113-8E76-4910-9C61-95BC82BB3003}.Release|x86.ActiveCfg = Release|Any CPU
+ {33D71368-9175-4A74-831E-11ACE6D815EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {33D71368-9175-4A74-831E-11ACE6D815EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {33D71368-9175-4A74-831E-11ACE6D815EE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {33D71368-9175-4A74-831E-11ACE6D815EE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {33D71368-9175-4A74-831E-11ACE6D815EE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {33D71368-9175-4A74-831E-11ACE6D815EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {33D71368-9175-4A74-831E-11ACE6D815EE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {33D71368-9175-4A74-831E-11ACE6D815EE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {33D71368-9175-4A74-831E-11ACE6D815EE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {33D71368-9175-4A74-831E-11ACE6D815EE}.Release|x86.ActiveCfg = Release|Any CPU
+ {D30CDA1E-CD18-42CE-934F-F33162F1B6EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D30CDA1E-CD18-42CE-934F-F33162F1B6EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D30CDA1E-CD18-42CE-934F-F33162F1B6EC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {D30CDA1E-CD18-42CE-934F-F33162F1B6EC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {D30CDA1E-CD18-42CE-934F-F33162F1B6EC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D30CDA1E-CD18-42CE-934F-F33162F1B6EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D30CDA1E-CD18-42CE-934F-F33162F1B6EC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D30CDA1E-CD18-42CE-934F-F33162F1B6EC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {D30CDA1E-CD18-42CE-934F-F33162F1B6EC}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {D30CDA1E-CD18-42CE-934F-F33162F1B6EC}.Release|x86.ActiveCfg = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {01F860AB-80DD-4AB4-B495-D5B20A493E60} = {35BED7DE-CA6F-41EA-91E3-25CA7295C7B9}
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6} = {35BED7DE-CA6F-41EA-91E3-25CA7295C7B9}
+ {8C045113-8E76-4910-9C61-95BC82BB3003} = {35BED7DE-CA6F-41EA-91E3-25CA7295C7B9}
+ {33D71368-9175-4A74-831E-11ACE6D815EE} = {35BED7DE-CA6F-41EA-91E3-25CA7295C7B9}
+ {D30CDA1E-CD18-42CE-934F-F33162F1B6EC} = {35BED7DE-CA6F-41EA-91E3-25CA7295C7B9}
+ {A08C9073-CB43-409E-9A5E-4BE2CA687FE3} = {547020C0-976F-4542-81CE-7FD465595614}
+ {FB272D58-D5FD-41D1-9E02-BCE17FD97F0E} = {547020C0-976F-4542-81CE-7FD465595614}
+ EndGlobalSection
+EndGlobal
diff --git a/Data.sln.DotSettings b/Data.sln.DotSettings
new file mode 100644
index 00000000000..c954f98885a
--- /dev/null
+++ b/Data.sln.DotSettings
@@ -0,0 +1,67 @@
+
+
+ <?xml version="1.0" encoding="utf-16"?><Profile name="EntityFramework"><HtmlReformatCode>True</HtmlReformatCode><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_TO_IMPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_IMPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_IMPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><XMLReformatCode>True</XMLReformatCode><CSUpdateFileHeader>True</CSUpdateFileHeader><CSharpFormatDocComments>True</CSharpFormatDocComments></Profile>
+
+ EntityFramework
+ EntityFramework
+ False
+ False
+ False
+ SEPARATE
+ True
+ True
+ True
+ ALWAYS_ADD
+ ALWAYS_ADD
+ ALWAYS_ADD
+ ALWAYS_ADD
+ ALWAYS_ADD
+ ALWAYS_ADD
+ True
+ 1
+ 1
+ True
+ False
+ False
+ False
+ True
+ LINE_BREAK
+ False
+ True
+ False
+ True
+ True
+ True
+ True
+ True
+ 140
+ CHOP_ALWAYS
+ True
+
+ True
+ False
+ True
+ True
+ True
+ Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+ True
+ True
+ True
+ Side by side
+ Side by side
+ False
+ False
+ False
+ True
+ False
+ False
+ True
+ False
+ False
+ True
+ $object$_On$event$
+ <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
+ $object$_On$event$
+ True
+ True
\ No newline at end of file
diff --git a/NuGet.Config b/NuGet.Config
new file mode 100644
index 00000000000..ab583b0ff77
--- /dev/null
+++ b/NuGet.Config
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build.cmd b/build.cmd
new file mode 100644
index 00000000000..c3b24620191
--- /dev/null
+++ b/build.cmd
@@ -0,0 +1,16 @@
+@echo off
+cd %~dp0
+
+IF EXIST .nuget\NuGet.exe goto restore
+echo Downloading latest version of NuGet.exe...
+md .nuget
+@powershell -NoProfile -ExecutionPolicy unrestricted -Command "$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest 'https://www.nuget.org/nuget.exe' -OutFile '.nuget\NuGet.exe'"
+
+:restore
+IF EXIST build goto run
+.nuget\NuGet.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre
+xcopy packages\KoreBuild\build build\ /Y
+.nuget\NuGet.exe install Sake -version 0.2 -o packages
+
+:run
+packages\Sake.0.2\tools\Sake.exe -I build -f makefile.shade %*
diff --git a/makefile.shade b/makefile.shade
new file mode 100644
index 00000000000..6357ea2841d
--- /dev/null
+++ b/makefile.shade
@@ -0,0 +1,7 @@
+
+var VERSION='0.1'
+var FULL_VERSION='0.1'
+var AUTHORS='Microsoft'
+
+use-standard-lifecycle
+k-standard-goals
diff --git a/src/Microsoft.Data.Core/Metadata/Annotation.cs b/src/Microsoft.Data.Core/Metadata/Annotation.cs
new file mode 100644
index 00000000000..2fd48debc95
--- /dev/null
+++ b/src/Microsoft.Data.Core/Metadata/Annotation.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.Data.Core.Metadata
+{
+ using JetBrains.Annotations;
+ using Microsoft.Data.Core.Utilities;
+
+ public class Annotation : MetadataBase
+ {
+ private readonly object _value;
+
+ public Annotation([NotNull] string name, object value)
+ : base(name)
+ {
+ Check.NotNull(value, "value");
+
+ _value = value;
+ }
+
+ public virtual object Value
+ {
+ get { return _value; }
+ }
+ }
+}
diff --git a/src/Microsoft.Data.Core/Metadata/Entity.cs b/src/Microsoft.Data.Core/Metadata/Entity.cs
new file mode 100644
index 00000000000..3c94343f0fb
--- /dev/null
+++ b/src/Microsoft.Data.Core/Metadata/Entity.cs
@@ -0,0 +1,95 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.Data.Core.Metadata
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Immutable;
+ using System.Linq;
+ using System.Reflection;
+ using JetBrains.Annotations;
+ using Microsoft.Data.Core.Utilities;
+
+ public class Entity : MetadataBase
+ {
+ private readonly Type _type;
+
+ private readonly LazyRef> _properties
+ = new LazyRef>(() => ImmutableDictionary.Empty);
+
+ private readonly LazyRef> _keyProperties
+ = new LazyRef>(() => ImmutableList.Empty);
+
+ public Entity([NotNull] Type type)
+ : base(Check.NotNull(type, "type", t => t.Name))
+ {
+ _type = type;
+ }
+
+ public virtual Type Type
+ {
+ get { return _type; }
+ }
+
+ public virtual IEnumerable Key
+ {
+ get
+ {
+ return _keyProperties.HasValue
+ ? _keyProperties.Value
+ : Enumerable.Empty();
+ }
+ [param: NotNull]
+ set
+ {
+ Check.NotNull(value, "value");
+
+ _keyProperties.ExchangeValue(l => l.Clear().AddRange(value));
+ _properties.ExchangeValue(ps => ps.SetItems(value.ToDictionary(p => p.PropertyInfo)));
+ }
+ }
+
+ public virtual void AddProperty([NotNull] Property property)
+ {
+ Check.NotNull(property, "property");
+
+ _properties.ExchangeValue(l => l.Add(property.PropertyInfo, property));
+ }
+
+ public virtual void RemoveProperty([NotNull] Property property)
+ {
+ Check.NotNull(property, "property");
+
+ if (_properties.HasValue)
+ {
+ _properties.ExchangeValue(l => l.Remove(property.PropertyInfo));
+
+ if (_keyProperties.HasValue)
+ {
+ _keyProperties.ExchangeValue(l => l.Remove(property));
+ }
+ }
+ }
+
+ public virtual Property Property([NotNull] PropertyInfo propertyInfo)
+ {
+ Check.NotNull(propertyInfo, "propertyInfo");
+
+ Property property;
+ return _properties.HasValue
+ && _properties.Value.TryGetValue(propertyInfo, out property)
+ ? property
+ : null;
+ }
+
+ public virtual IEnumerable Properties
+ {
+ get
+ {
+ return _properties.HasValue
+ ? _properties.Value.Values.OrderByOrdinal(e => e.Name)
+ : Enumerable.Empty();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.Data.Core/Metadata/MetadataBase.cs b/src/Microsoft.Data.Core/Metadata/MetadataBase.cs
new file mode 100644
index 00000000000..2e045092719
--- /dev/null
+++ b/src/Microsoft.Data.Core/Metadata/MetadataBase.cs
@@ -0,0 +1,98 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.Data.Core.Metadata
+{
+ using System.Collections.Generic;
+ using System.Collections.Immutable;
+ using System.Diagnostics;
+ using System.Linq;
+ using JetBrains.Annotations;
+ using Microsoft.Data.Core.Utilities;
+
+ [DebuggerDisplay("{Name}")]
+ public abstract class MetadataBase
+ {
+ private readonly string _name;
+
+ private string _storageName;
+
+ private readonly LazyRef> _annotations
+ = new LazyRef>(() => ImmutableDictionary.Empty);
+
+ protected MetadataBase()
+ {
+ }
+
+ protected MetadataBase([NotNull] string name)
+ {
+ Check.NotEmpty(name, "name");
+
+ _name = name;
+ }
+
+ public virtual string Name
+ {
+ get { return _name; }
+ }
+
+ public virtual string StorageName
+ {
+ get { return _storageName ?? Name; }
+ [param: NotNull]
+ set
+ {
+ Check.NotEmpty(value, "value");
+
+ _storageName = value;
+ }
+ }
+
+ public virtual void AddAnnotation([NotNull] Annotation annotation)
+ {
+ Check.NotNull(annotation, "annotation");
+
+ _annotations.ExchangeValue(d => d.Add(annotation.Name, annotation));
+ }
+
+ public virtual void RemoveAnnotation([NotNull] Annotation annotation)
+ {
+ Check.NotNull(annotation, "annotation");
+
+ _annotations.ExchangeValue(l => l.Remove(annotation.Name));
+ }
+
+ public virtual object this[[NotNull] string annotation]
+ {
+ get
+ {
+ Check.NotEmpty(annotation, "annotation");
+
+ Annotation value;
+ return _annotations.HasValue
+ && _annotations.Value.TryGetValue(annotation, out value)
+ ? value.Value
+ : null;
+ }
+ [param: NotNull]
+ set
+ {
+ Check.NotEmpty(annotation, "annotation");
+ Check.NotNull(value, "value");
+
+ _annotations.ExchangeValue(l => l.Remove(annotation));
+
+ AddAnnotation(new Annotation(annotation, value));
+ }
+ }
+
+ public virtual IEnumerable Annotations
+ {
+ get
+ {
+ return _annotations.HasValue
+ ? _annotations.Value.Values.OrderByOrdinal(e => e.Name)
+ : Enumerable.Empty();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.Data.Core/Metadata/Model.cs b/src/Microsoft.Data.Core/Metadata/Model.cs
new file mode 100644
index 00000000000..a271824cad7
--- /dev/null
+++ b/src/Microsoft.Data.Core/Metadata/Model.cs
@@ -0,0 +1,59 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.Data.Core.Metadata
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Immutable;
+ using System.Linq;
+ using JetBrains.Annotations;
+ using Microsoft.Data.Core.Utilities;
+
+ public class Model : MetadataBase
+ {
+ private readonly LazyRef> _entities
+ = new LazyRef>(() => ImmutableDictionary.Empty);
+
+ public virtual void AddEntity([NotNull] Entity entity)
+ {
+ Check.NotNull(entity, "entity");
+
+ _entities.ExchangeValue(d => d.Add(entity.Type, entity));
+ }
+
+ public virtual void RemoveEntity([NotNull] Entity entity)
+ {
+ Check.NotNull(entity, "entity");
+
+ _entities.ExchangeValue(l => l.Remove(entity.Type));
+ }
+
+ public virtual Entity Entity([NotNull] object @object)
+ {
+ Check.NotNull(@object, "object");
+
+ return Entity(@object.GetType());
+ }
+
+ public virtual Entity Entity([NotNull] Type type)
+ {
+ Check.NotNull(type, "type");
+
+ Entity value;
+ return _entities.HasValue
+ && _entities.Value.TryGetValue(type, out value)
+ ? value
+ : null;
+ }
+
+ public virtual IEnumerable Entities
+ {
+ get
+ {
+ return _entities.HasValue
+ ? _entities.Value.Values.OrderByOrdinal(e => e.Name)
+ : Enumerable.Empty();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.Data.Core/Metadata/ModelBuilder.cs b/src/Microsoft.Data.Core/Metadata/ModelBuilder.cs
new file mode 100644
index 00000000000..706fd1ec70f
--- /dev/null
+++ b/src/Microsoft.Data.Core/Metadata/ModelBuilder.cs
@@ -0,0 +1,147 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.Data.Core.Metadata
+{
+ using System;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using JetBrains.Annotations;
+ using Microsoft.Data.Core.Utilities;
+
+ public class ModelBuilder
+ {
+ private readonly Model _model;
+
+ public ModelBuilder([NotNull] Model model)
+ {
+ Check.NotNull(model, "model");
+
+ _model = model;
+ }
+
+ public virtual EntityBuilder Entity()
+ {
+ var type = typeof(T);
+ var entity = _model.Entity(type);
+
+ if (entity == null)
+ {
+ _model.AddEntity(entity = new Entity(type));
+ }
+
+ return new EntityBuilder(entity);
+ }
+
+ public virtual ModelBuilder Annotation([NotNull] string annotation, [NotNull] object value)
+ {
+ Check.NotEmpty(annotation, "annotation");
+ Check.NotNull(value, "value");
+
+ _model[annotation] = value;
+
+ return this;
+ }
+
+ public class EntityBuilder
+ {
+ private readonly Entity _entity;
+
+ internal EntityBuilder()
+ {
+ }
+
+ internal EntityBuilder(Entity entity)
+ {
+ DebugCheck.NotNull(entity);
+
+ _entity = entity;
+ }
+
+ public EntityBuilder Key([NotNull] Expression> keyExpression)
+ {
+ Check.NotNull(keyExpression, "keyExpression");
+
+ var propertyInfos = keyExpression.GetPropertyAccessList();
+
+ _entity.Key
+ = propertyInfos
+ .Select(pi => _entity.Property(pi) ?? new Property(pi));
+
+ return this;
+ }
+
+ public EntityBuilder Annotation([NotNull] string annotation, [NotNull] object value)
+ {
+ Check.NotEmpty(annotation, "annotation");
+ Check.NotNull(value, "value");
+
+ _entity[annotation] = value;
+
+ return this;
+ }
+
+ public EntityBuilder Properties([NotNull] Action propertiesBuilder)
+ {
+ Check.NotNull(propertiesBuilder, "propertiesBuilder");
+
+ propertiesBuilder(new PropertiesBuilder(_entity));
+
+ return this;
+ }
+
+ public class PropertiesBuilder
+ {
+ private readonly Entity _entity;
+
+ internal PropertiesBuilder()
+ {
+ }
+
+ internal PropertiesBuilder(Entity entity)
+ {
+ _entity = entity;
+ }
+
+ public virtual PropertyBuilder Property([NotNull] Expression> propertyExpression)
+ {
+ var propertyInfo = propertyExpression.GetPropertyAccess();
+
+ var property = _entity.Property(propertyInfo);
+
+ if (property == null)
+ {
+ _entity.AddProperty(property = new Property(propertyInfo));
+ }
+
+ return new PropertyBuilder(property);
+ }
+
+ public class PropertyBuilder
+ {
+ private readonly Property _property;
+
+ internal PropertyBuilder()
+ {
+ }
+
+ internal PropertyBuilder(Property property)
+ {
+ DebugCheck.NotNull(property);
+
+ _property = property;
+ }
+
+ public PropertyBuilder Annotation([NotNull] string annotation, [NotNull] object value)
+ {
+ Check.NotEmpty(annotation, "annotation");
+ Check.NotNull(value, "value");
+
+ _property[annotation] = value;
+
+ return this;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.Data.Core/Metadata/Property.cs b/src/Microsoft.Data.Core/Metadata/Property.cs
new file mode 100644
index 00000000000..f5d14deb70a
--- /dev/null
+++ b/src/Microsoft.Data.Core/Metadata/Property.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.Data.Core.Metadata
+{
+ using System.Reflection;
+ using JetBrains.Annotations;
+ using Microsoft.Data.Core.Utilities;
+
+ public class Property : MetadataBase
+ {
+ private readonly PropertyInfo _propertyInfo;
+
+ public Property([NotNull] PropertyInfo propertyInfo)
+ : base(Check.NotNull(propertyInfo, "propertyInfo", p => p.Name))
+ {
+ _propertyInfo = propertyInfo;
+ }
+
+ public virtual PropertyInfo PropertyInfo
+ {
+ get { return _propertyInfo; }
+ }
+ }
+}
diff --git a/src/Microsoft.Data.Core/Microsoft.Data.Core.csproj b/src/Microsoft.Data.Core/Microsoft.Data.Core.csproj
new file mode 100644
index 00000000000..1838aab418d
--- /dev/null
+++ b/src/Microsoft.Data.Core/Microsoft.Data.Core.csproj
@@ -0,0 +1,94 @@
+
+
+
+
+ 12.0
+ Debug
+ AnyCPU
+ {6A531BB3-DF57-4743-AF3B-4BE431B67DE6}
+ Library
+ Properties
+ Microsoft.Data.Core
+ Microsoft.Data.Core
+ v4.6
+ Profile44
+ 512
+ {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+ ExtendedDesignGuidelineRules.ruleset
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+ true
+
+
+
+
+ ..\..\packages\Microsoft.Bcl.Immutable.1.0.30\lib\portable-net45+win8+wp8\System.Collections.Immutable.dll
+
+
+
+
+
+
+
+
+
+
+ Code
+
+
+
+
+
+ Code
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.tt
+
+
+ TextTemplatingFileGenerator
+ Resources.cs
+
+
+ Designer
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Microsoft.Data.Core/Properties/InternalsVisibleTo.cs b/src/Microsoft.Data.Core/Properties/InternalsVisibleTo.cs
new file mode 100644
index 00000000000..470693c2b90
--- /dev/null
+++ b/src/Microsoft.Data.Core/Properties/InternalsVisibleTo.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+using System.Runtime.CompilerServices;
+
+#if !INTERNALS_INVISIBLE
+
+[assembly: InternalsVisibleTo("Microsoft.Data.Core.UnitTest")]
+
+// for Moq
+
+[assembly:
+ InternalsVisibleTo(
+ "DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7"
+ )]
+
+#endif
diff --git a/src/Microsoft.Data.Core/Resources/Resources.cs b/src/Microsoft.Data.Core/Resources/Resources.cs
new file mode 100644
index 00000000000..6bf9dd284bb
--- /dev/null
+++ b/src/Microsoft.Data.Core/Resources/Resources.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.Data.Core.Resources
+{
+ using System.Globalization;
+ using System.Reflection;
+ using System.Resources;
+
+ internal static class Strings
+ {
+ private static readonly ResourceManager _resourceManager
+ = new ResourceManager("Microsoft.Data.Core.Resources.Resources", typeof(Strings).GetTypeInfo().Assembly);
+
+ ///
+ /// "The argument '{0}' cannot be null, empty or contain only white space."
+ ///
+ internal static string ArgumentIsNullOrWhitespace(object p0)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("ArgumentIsNullOrWhitespace"), p0);
+ }
+
+ ///
+ /// "The properties expression '{0}' is not valid. The expression should represent a property: C#: 't => t.MyProperty'
+ /// VB.Net: 'Function(t) t.MyProperty'. When specifying multiple properties use an anonymous type: C#: 't => new {{
+ /// t.MyProperty1, t.MyProperty2 }}' VB.Net: 'Function(t) New With {{ t.MyProperty1, t.MyProperty2 }}'."
+ ///
+ internal static string InvalidPropertiesExpression(object p0)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("InvalidPropertiesExpression"), p0);
+ }
+
+ ///
+ /// "The expression '{0}' is not a valid property expression. The expression should represent a property: C#: 't =>
+ /// t.MyProperty' VB.Net: 'Function(t) t.MyProperty'."
+ ///
+ internal static string InvalidPropertyExpression(object p0)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("InvalidPropertyExpression"), p0);
+ }
+
+ private static string GetString(string name)
+ {
+ return _resourceManager.GetString(name)
+ ?? "ERROR: Resource '" + name + "' NOT FOUND!";
+ }
+ }
+}
diff --git a/src/Microsoft.Data.Core/Resources/Resources.resx b/src/Microsoft.Data.Core/Resources/Resources.resx
new file mode 100644
index 00000000000..dd1d565e73a
--- /dev/null
+++ b/src/Microsoft.Data.Core/Resources/Resources.resx
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ The argument '{0}' cannot be null, empty or contain only white space.
+
+
+ The properties expression '{0}' is not valid. The expression should represent a property: C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'. When specifying multiple properties use an anonymous type: C#: 't => new {{ t.MyProperty1, t.MyProperty2 }}' VB.Net: 'Function(t) New With {{ t.MyProperty1, t.MyProperty2 }}'.
+
+
+ The expression '{0}' is not a valid property expression. The expression should represent a property: C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'.
+
+
\ No newline at end of file
diff --git a/src/Microsoft.Data.Core/Resources/Resources.tt b/src/Microsoft.Data.Core/Resources/Resources.tt
new file mode 100644
index 00000000000..31efc795b62
--- /dev/null
+++ b/src/Microsoft.Data.Core/Resources/Resources.tt
@@ -0,0 +1,91 @@
+<#@ template debug="true" hostspecific="true" language="C#" #>
+<#@ assembly name="System.Core" #>
+<#@ assembly name="System.Windows.Forms" #>
+<#@ import namespace="System" #>
+<#@ import namespace="System.Collections" #>
+<#@ import namespace="System.Collections.Generic" #>
+<#@ import namespace="System.IO" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Resources" #>
+<#@ import namespace="System.Text.RegularExpressions" #>
+<#@ output extension=".cs" #>
+<#
+
+var parameterMatcher = new Regex(@"\{(\d)\}");
+var lines = new List>();
+
+using (var resxReader = new ResXResourceReader(Path.ChangeExtension(Host.TemplateFile, "resx")))
+{
+ resxReader.UseResXDataNodes = true;
+
+ foreach (DictionaryEntry entry in resxReader)
+ {
+ var node = (ResXDataNode)entry.Value;
+ var value = (string)node.GetValue((System.ComponentModel.Design.ITypeResolutionService)null);
+
+ var matchedArgs
+ = parameterMatcher.Matches(value)
+ .Cast()
+ .Select(m => Convert.ToInt32(m.Groups[1].Value))
+ .ToArray();
+
+ var argGenerator
+ = new object[matchedArgs.Any() ? matchedArgs.Max() + 1 : 0];
+
+ lines.Add(Tuple.Create(
+ node.Name,
+ value,
+ node.Comment.StartsWith("## ExceptionType=") ? node.Comment.Substring(17) : null,
+ argGenerator.Any(),
+ string.Join(", ", argGenerator.Select((_, i) => "p" + i)),
+ "(" + string.Join(", ", argGenerator.Select((_, i) => "object p" + i)) + ")"
+ ));
+ }
+}
+
+string outputNamespace = Host.ResolveParameterValue("directiveId", "namespaceDirectiveProcessor", "namespaceHint") ?? string.Empty;
+#>
+//
+
+namespace <#= outputNamespace #>
+{
+ using System.Globalization;
+ using System.Reflection;
+ using System.Resources;
+
+ internal static class Strings
+ {
+ private static readonly ResourceManager _resourceManager
+ = new ResourceManager("Microsoft.Data.Core.Resources.Resources", typeof(Strings).GetTypeInfo().Assembly);
+<#
+ foreach (var line in lines)
+ {
+ #>
+
+ ///
+ /// "<#= line.Item2 #>"
+ ///
+ internal static string <#= line.Item1 #><#= line.Item4 ? line.Item6 : string.Empty #>
+ {
+ <#
+ if (!line.Item4)
+ {
+ #>get { return GetString("<#= line.Item1 #>"); }
+<#
+ }
+ else
+ {
+ #>return string.Format(CultureInfo.CurrentCulture, GetString("<#= line.Item1 #>"), <#= line.Item5 #>);
+<#
+ }#>
+ }
+<#
+ }#>
+
+ private static string GetString(string name)
+ {
+ return _resourceManager.GetString(name)
+ ?? "ERROR: Resource '" + name + "' NOT FOUND!";
+ }
+ }
+}
diff --git a/src/Microsoft.Data.Core/Utilities/Check.cs b/src/Microsoft.Data.Core/Utilities/Check.cs
new file mode 100644
index 00000000000..eb6777a7e51
--- /dev/null
+++ b/src/Microsoft.Data.Core/Utilities/Check.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.Data.Core.Utilities
+{
+ using System;
+ using System.Diagnostics;
+ using JetBrains.Annotations;
+ using Microsoft.Data.Core.Resources;
+
+ [DebuggerStepThrough]
+ public static class Check
+ {
+ public static void NotNull(object value, [InvokerParameterName] [NotNull] string parameterName)
+ {
+ NotEmpty(parameterName, "parameterName");
+
+ if (value == null)
+ {
+ throw new ArgumentNullException(parameterName);
+ }
+ }
+
+ public static S NotNull(T value, [InvokerParameterName] [NotNull] string parameterName, Func result)
+ {
+ NotNull(value, parameterName);
+
+ return result(value);
+ }
+
+// public static T Inline(T value, Action checker)
+// where T : class
+// {
+// checker();
+//
+// return value;
+// }
+
+ public static string NotEmpty(string value, [InvokerParameterName] [NotNull] string parameterName)
+ {
+ if (string.IsNullOrWhiteSpace(parameterName))
+ {
+ throw new ArgumentException(Strings.ArgumentIsNullOrWhitespace("parameterName"));
+ }
+
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ throw new ArgumentException(Strings.ArgumentIsNullOrWhitespace(parameterName));
+ }
+
+ return value;
+ }
+ }
+}
diff --git a/src/Microsoft.Data.Core/Utilities/CodeAnnotations.cs b/src/Microsoft.Data.Core/Utilities/CodeAnnotations.cs
new file mode 100644
index 00000000000..66ef0723fe1
--- /dev/null
+++ b/src/Microsoft.Data.Core/Utilities/CodeAnnotations.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace JetBrains.Annotations
+{
+ using System;
+
+ [AttributeUsage(
+ AttributeTargets.Method | AttributeTargets.Parameter |
+ AttributeTargets.Property | AttributeTargets.Delegate |
+ AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
+ public sealed class NotNullAttribute : Attribute
+ {
+ }
+
+ [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
+ public sealed class InvokerParameterNameAttribute : Attribute
+ {
+ }
+}
diff --git a/src/Microsoft.Data.Core/Utilities/DebugCheck.cs b/src/Microsoft.Data.Core/Utilities/DebugCheck.cs
new file mode 100644
index 00000000000..806b3d7a211
--- /dev/null
+++ b/src/Microsoft.Data.Core/Utilities/DebugCheck.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.Data.Core.Utilities
+{
+ using System.Diagnostics;
+
+ [DebuggerStepThrough]
+ public static class DebugCheck
+ {
+ [Conditional("DEBUG")]
+ public static void NotNull(object value)
+ {
+ Debug.Assert(value != null);
+ }
+
+ [Conditional("DEBUG")]
+ public static void NotEmpty(string value)
+ {
+ Debug.Assert(!string.IsNullOrWhiteSpace(value));
+ }
+ }
+}
diff --git a/src/Microsoft.Data.Core/Utilities/EnumerableExtensions.cs b/src/Microsoft.Data.Core/Utilities/EnumerableExtensions.cs
new file mode 100644
index 00000000000..16dd9748dcd
--- /dev/null
+++ b/src/Microsoft.Data.Core/Utilities/EnumerableExtensions.cs
@@ -0,0 +1,115 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.Data.Core.Utilities
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using JetBrains.Annotations;
+
+ [DebuggerStepThrough]
+ public static class EnumerableExtensions
+ {
+ public static IOrderedEnumerable OrderByOrdinal(
+ this IEnumerable source, Func keySelector)
+ {
+ Check.NotNull(source, "source");
+ Check.NotNull(keySelector, "keySelector");
+
+ return source.OrderBy(keySelector, StringComparer.Ordinal);
+ }
+
+ public static IEnumerable Distinct(
+ [NotNull] this IEnumerable source, [NotNull] Func comparer)
+ where T : class
+ {
+ Check.NotNull(source, "source");
+ Check.NotNull(comparer, "comparer");
+
+ return source.Distinct(new DynamicEqualityComparer(comparer));
+ }
+
+ private sealed class DynamicEqualityComparer : IEqualityComparer
+ where T : class
+ {
+ private readonly Func _func;
+
+ public DynamicEqualityComparer(Func func)
+ {
+ DebugCheck.NotNull(func);
+
+ _func = func;
+ }
+
+ public bool Equals(T x, T y)
+ {
+ return _func(x, y);
+ }
+
+ public int GetHashCode(T obj)
+ {
+ return 0; // force Equals
+ }
+ }
+
+ public static string Join([NotNull] this IEnumerable