diff --git a/App.sln b/App.sln
index 289fb8aeb6..0e7775d7bd 100644
--- a/App.sln
+++ b/App.sln
@@ -15,36 +15,96 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ODataServiceTests", "ODataS
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataSourceGenerator", "ODataService\DataSourceGenerator\DataSourceGenerator.csproj", "{E6271DA9-A12E-4484-AC50-43F55C1F2DF5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneratedDataSourceTests", "GeneratedDataSourceTests\GeneratedDataSourceTests.csproj", "{7DECA341-442D-41D5-89B4-B3BCD3676758}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GeneratedDataSourceTests", "GeneratedDataSourceTests\GeneratedDataSourceTests.csproj", "{7DECA341-442D-41D5-89B4-B3BCD3676758}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project2", "Project2\Project2.csproj", "{7D4313F7-300D-405A-A92B-285EE1045FAC}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiService", "ODataService\ODataService\ApiService.csproj", "{B379640E-9064-438D-8DA5-6F7B394C2C46}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RESTier", "RESTier", "{0193DBDA-F7D5-4A71-8097-13D74DF3DE58}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Restier.Core", "ODataService\RESTier\src\Microsoft.Restier.Core\Microsoft.Restier.Core.csproj", "{052E17C4-C151-4964-A873-979682F8619B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Restier.AspNet", "ODataService\RESTier\src\Microsoft.Restier.AspNet\Microsoft.Restier.AspNet.csproj", "{573D523D-AF49-460B-98C7-AB9FB930AC60}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.OData", "ODataService\WebAPI\src\Microsoft.AspNet.OData\Microsoft.AspNet.OData.csproj", "{A6F9775D-F7E2-424E-8363-79644A73038F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicService", "ODataService\DynamicService\DynamicService.csproj", "{E8072054-3D92-4321-86B1-E038616AAA3E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataSourceManager", "ODataService\DataSourceManager\DataSourceManager.csproj", "{BED3CAAD-C9A8-4F29-B35A-3D73A9DECE87}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ CodeAnalysis|Any CPU = CodeAnalysis|Any CPU
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {413A6974-270D-4F33-BA5D-BDB2080F684C}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {413A6974-270D-4F33-BA5D-BDB2080F684C}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
{413A6974-270D-4F33-BA5D-BDB2080F684C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{413A6974-270D-4F33-BA5D-BDB2080F684C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{413A6974-270D-4F33-BA5D-BDB2080F684C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{413A6974-270D-4F33-BA5D-BDB2080F684C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1978960D-EFF1-4AB1-902B-0DE7BEADA5A1}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {1978960D-EFF1-4AB1-902B-0DE7BEADA5A1}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
{1978960D-EFF1-4AB1-902B-0DE7BEADA5A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1978960D-EFF1-4AB1-902B-0DE7BEADA5A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1978960D-EFF1-4AB1-902B-0DE7BEADA5A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1978960D-EFF1-4AB1-902B-0DE7BEADA5A1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E6271DA9-A12E-4484-AC50-43F55C1F2DF5}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {E6271DA9-A12E-4484-AC50-43F55C1F2DF5}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
{E6271DA9-A12E-4484-AC50-43F55C1F2DF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E6271DA9-A12E-4484-AC50-43F55C1F2DF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6271DA9-A12E-4484-AC50-43F55C1F2DF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6271DA9-A12E-4484-AC50-43F55C1F2DF5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7DECA341-442D-41D5-89B4-B3BCD3676758}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {7DECA341-442D-41D5-89B4-B3BCD3676758}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
{7DECA341-442D-41D5-89B4-B3BCD3676758}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DECA341-442D-41D5-89B4-B3BCD3676758}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DECA341-442D-41D5-89B4-B3BCD3676758}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DECA341-442D-41D5-89B4-B3BCD3676758}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7D4313F7-300D-405A-A92B-285EE1045FAC}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
{7D4313F7-300D-405A-A92B-285EE1045FAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D4313F7-300D-405A-A92B-285EE1045FAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D4313F7-300D-405A-A92B-285EE1045FAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D4313F7-300D-405A-A92B-285EE1045FAC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B379640E-9064-438D-8DA5-6F7B394C2C46}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU
+ {B379640E-9064-438D-8DA5-6F7B394C2C46}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU
+ {B379640E-9064-438D-8DA5-6F7B394C2C46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B379640E-9064-438D-8DA5-6F7B394C2C46}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B379640E-9064-438D-8DA5-6F7B394C2C46}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B379640E-9064-438D-8DA5-6F7B394C2C46}.Release|Any CPU.Build.0 = Release|Any CPU
+ {052E17C4-C151-4964-A873-979682F8619B}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU
+ {052E17C4-C151-4964-A873-979682F8619B}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU
+ {052E17C4-C151-4964-A873-979682F8619B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {052E17C4-C151-4964-A873-979682F8619B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {052E17C4-C151-4964-A873-979682F8619B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {052E17C4-C151-4964-A873-979682F8619B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {573D523D-AF49-460B-98C7-AB9FB930AC60}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU
+ {573D523D-AF49-460B-98C7-AB9FB930AC60}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU
+ {573D523D-AF49-460B-98C7-AB9FB930AC60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {573D523D-AF49-460B-98C7-AB9FB930AC60}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {573D523D-AF49-460B-98C7-AB9FB930AC60}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {573D523D-AF49-460B-98C7-AB9FB930AC60}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A6F9775D-F7E2-424E-8363-79644A73038F}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU
+ {A6F9775D-F7E2-424E-8363-79644A73038F}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU
+ {A6F9775D-F7E2-424E-8363-79644A73038F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A6F9775D-F7E2-424E-8363-79644A73038F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A6F9775D-F7E2-424E-8363-79644A73038F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A6F9775D-F7E2-424E-8363-79644A73038F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E8072054-3D92-4321-86B1-E038616AAA3E}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8072054-3D92-4321-86B1-E038616AAA3E}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU
+ {E8072054-3D92-4321-86B1-E038616AAA3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8072054-3D92-4321-86B1-E038616AAA3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E8072054-3D92-4321-86B1-E038616AAA3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E8072054-3D92-4321-86B1-E038616AAA3E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BED3CAAD-C9A8-4F29-B35A-3D73A9DECE87}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU
+ {BED3CAAD-C9A8-4F29-B35A-3D73A9DECE87}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU
+ {BED3CAAD-C9A8-4F29-B35A-3D73A9DECE87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BED3CAAD-C9A8-4F29-B35A-3D73A9DECE87}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BED3CAAD-C9A8-4F29-B35A-3D73A9DECE87}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BED3CAAD-C9A8-4F29-B35A-3D73A9DECE87}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -54,6 +114,13 @@ Global
{1978960D-EFF1-4AB1-902B-0DE7BEADA5A1} = {957D9415-CE59-47B5-ABB8-A5EA12863F4E}
{E6271DA9-A12E-4484-AC50-43F55C1F2DF5} = {621819DE-DDB7-41FC-A874-757464A90050}
{7DECA341-442D-41D5-89B4-B3BCD3676758} = {957D9415-CE59-47B5-ABB8-A5EA12863F4E}
+ {B379640E-9064-438D-8DA5-6F7B394C2C46} = {621819DE-DDB7-41FC-A874-757464A90050}
+ {0193DBDA-F7D5-4A71-8097-13D74DF3DE58} = {621819DE-DDB7-41FC-A874-757464A90050}
+ {052E17C4-C151-4964-A873-979682F8619B} = {0193DBDA-F7D5-4A71-8097-13D74DF3DE58}
+ {573D523D-AF49-460B-98C7-AB9FB930AC60} = {0193DBDA-F7D5-4A71-8097-13D74DF3DE58}
+ {A6F9775D-F7E2-424E-8363-79644A73038F} = {0193DBDA-F7D5-4A71-8097-13D74DF3DE58}
+ {E8072054-3D92-4321-86B1-E038616AAA3E} = {621819DE-DDB7-41FC-A874-757464A90050}
+ {BED3CAAD-C9A8-4F29-B35A-3D73A9DECE87} = {621819DE-DDB7-41FC-A874-757464A90050}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {736D9CFE-B80E-445C-8328-D46C08F1FC8D}
diff --git a/ODataService/DataSourceManager/DataSourceManager.csproj b/ODataService/DataSourceManager/DataSourceManager.csproj
new file mode 100644
index 0000000000..8f1668c92f
--- /dev/null
+++ b/ODataService/DataSourceManager/DataSourceManager.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net7.0
+ false
+
+ Library
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ODataService/DataSourceManager/DataStoreManager/DefaultDataStoreManager.cs b/ODataService/DataSourceManager/DataStoreManager/DefaultDataStoreManager.cs
new file mode 100644
index 0000000000..959c346f7d
--- /dev/null
+++ b/ODataService/DataSourceManager/DataStoreManager/DefaultDataStoreManager.cs
@@ -0,0 +1,168 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Timers;
+
+namespace DataSourceManager.DataStoreManager
+{
+ ///
+ /// Default resource management class to manage resources.
+ /// Use a dictionary to easily access the resource by and make a constraint on the total number of resources.
+ /// Use a timer for each resource, when the resource live longer than , it will be destroyed automatically.
+ ///
+ public class DefaultDataStoreManager :IDataStoreManager where TDataStoreType : class, new()
+ {
+ ///
+ /// The max capacity of the resource container, this is a constraint for memory cost.
+ ///
+ public int MaxDataStoreInstanceCapacity { get; set; }
+
+ ///
+ /// The max life time of each resource. When the resource lives longer than that, it will be destroyed automatically.
+ /// Besides, when the resource container is full, the resource live longest will be destroyed.
+ ///
+ public TimeSpan MaxDataStoreInstanceLifeTime { get; set; }
+
+ private Dictionary _dataStoreDict = new Dictionary();
+
+ public DefaultDataStoreManager()
+ {
+ MaxDataStoreInstanceCapacity = 1000;
+ MaxDataStoreInstanceLifeTime = new TimeSpan(0, 15, 0);
+ }
+
+ public TDataStoreType ResetDataStoreInstance(TKey key)
+ {
+ if (_dataStoreDict.ContainsKey(key))
+ {
+ _dataStoreDict[key] = new DataStoreInstanceWrapper(key, MaxDataStoreInstanceLifeTime.TotalMilliseconds, ResouceTimeoutHandler);
+ }
+ else
+ {
+ AddDataStoreInstance(key);
+ }
+
+ return _dataStoreDict[key].DataStore;
+ }
+
+ public TDataStoreType GetDataStoreInstance(TKey key)
+ {
+ if (_dataStoreDict.ContainsKey(key))
+ {
+ _dataStoreDict[key].UpdateLastUsedDateTime();
+ }
+ else
+ {
+ AddDataStoreInstance(key);
+ }
+
+ return _dataStoreDict[key].DataStore;
+ }
+
+ private TDataStoreType AddDataStoreInstance(TKey key)
+ {
+ if (_dataStoreDict.Count >= MaxDataStoreInstanceCapacity)
+ {
+ // No resource lives longer than maxLifeTime, find the one lives longest and remove it.
+ var minLastUsedTime = DateTime.Now;
+ TKey minKey = default(TKey);
+
+ foreach (var val in _dataStoreDict)
+ {
+ var resourceLastUsedTime = val.Value.DataStoreLastUsedDateTime;
+ if (resourceLastUsedTime < minLastUsedTime)
+ {
+ minLastUsedTime = resourceLastUsedTime;
+ minKey = val.Key;
+ }
+ }
+
+ DeleteDataStoreInstance(minKey);
+ }
+
+ System.Diagnostics.Trace.TraceInformation("The resouce dictionary size right now is {0}", _dataStoreDict.Count);
+ _dataStoreDict.Add(key, new DataStoreInstanceWrapper(key, MaxDataStoreInstanceLifeTime.TotalMilliseconds, ResouceTimeoutHandler));
+ return _dataStoreDict[key].DataStore;
+ }
+
+ private DefaultDataStoreManager DeleteDataStoreInstance(TKey key)
+ {
+ if (_dataStoreDict.ContainsKey(key))
+ {
+ _dataStoreDict[key].StopTimer();
+ _dataStoreDict.Remove(key);
+ }
+
+ return this;
+ }
+
+ private void ResouceTimeoutHandler(object source, EventArgs e)
+ {
+ var resouceUnit = source as DataStoreInstanceWrapper;
+ if (resouceUnit != null)
+ {
+ System.Diagnostics.Trace.TraceInformation(resouceUnit.DatastoreKey + " timeout occured, now destroy it!");
+ DeleteDataStoreInstance(resouceUnit.DatastoreKey);
+ }
+ }
+
+ private class DataStoreInstanceWrapper
+ {
+ public TKey DatastoreKey { get; private set; }
+
+ public TDataStoreType DataStore { get; private set; }
+
+ public DateTime DataStoreLastUsedDateTime { get; private set; }
+
+ private Timer DataStoreTimer { get; set; }
+
+ private double _dataStoreLifeTime;
+
+ private EventHandler _timerTimeoutHandler;
+
+ public DataStoreInstanceWrapper(TKey key, double dataStoreLifeTime, EventHandler dataStoreTimeoutHandler)
+ {
+ DatastoreKey = key;
+ DataStore = new TDataStoreType();
+ DataStoreLastUsedDateTime = DateTime.Now;
+ _dataStoreLifeTime = dataStoreLifeTime;
+ _timerTimeoutHandler += dataStoreTimeoutHandler;
+ InitTimer();
+ }
+
+ public DataStoreInstanceWrapper UpdateLastUsedDateTime()
+ {
+ UpdateTimer();
+ DataStoreLastUsedDateTime = DateTime.Now;
+ return this;
+ }
+
+ public void StopTimer()
+ {
+ DataStoreTimer.Stop();
+ }
+
+ private Timer InitTimer()
+ {
+ DataStoreTimer = new Timer(_dataStoreLifeTime);
+ DataStoreTimer.Elapsed += (sender, args) =>
+ {
+ if (_timerTimeoutHandler != null)
+ {
+ _timerTimeoutHandler.Invoke(this, args);
+ }
+ };
+ DataStoreTimer.Start();
+ return DataStoreTimer;
+ }
+
+ private void UpdateTimer()
+ {
+ DataStoreTimer.Stop();
+ DataStoreTimer = InitTimer();
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/ODataService/DataSourceManager/DataStoreManager/IDataStoreManager.cs b/ODataService/DataSourceManager/DataStoreManager/IDataStoreManager.cs
new file mode 100644
index 0000000000..6923e1011f
--- /dev/null
+++ b/ODataService/DataSourceManager/DataStoreManager/IDataStoreManager.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+namespace DataSourceManager.DataStoreManager
+{
+ ///
+ /// This is a data store management interface to manage the datastore with its key.
+ ///
+ /// Type of the key to identify the data store.
+ /// Type of the real data store.
+ public interface IDataStoreManager
+ {
+ ///
+ /// Get the data store instance by its key.
+ ///
+ /// The key to identify the data store.
+ /// Return the data store with key.
+ TDataStoreType GetDataStoreInstance(TKey key);
+
+ ///
+ /// Find the resource by key and reset it to origin.
+ ///
+ /// The key to identify the data store.
+ /// Return the data store after reseting.
+ TDataStoreType ResetDataStoreInstance(TKey key);
+ }
+}
\ No newline at end of file
diff --git a/ODataService/DataSourceManager/DataStoreManager/SingleDataStoreManager.cs b/ODataService/DataSourceManager/DataStoreManager/SingleDataStoreManager.cs
new file mode 100644
index 0000000000..90e7cff99f
--- /dev/null
+++ b/ODataService/DataSourceManager/DataStoreManager/SingleDataStoreManager.cs
@@ -0,0 +1,28 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+namespace DataSourceManager.DataStoreManager
+{
+ ///
+ /// Data store manager for managing a single (i.e., read-only) datastore
+ ///
+ public class SingleDataStoreManager : IDataStoreManager where TDataStoreType : class, new()
+ {
+ private TDataStoreType dataStore;
+
+ public SingleDataStoreManager()
+ {
+ dataStore = new TDataStoreType();
+ }
+
+ public TDataStoreType ResetDataStoreInstance(TKey key)
+ {
+ return dataStore = new TDataStoreType();
+ }
+
+ public TDataStoreType GetDataStoreInstance(TKey key)
+ {
+ return dataStore;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ODataService/DataSourceManager/Properties/AssemblyInfo.cs b/ODataService/DataSourceManager/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..829ed9c6a9
--- /dev/null
+++ b/ODataService/DataSourceManager/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Microsoft.OData.Service.Library")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Microsoft.OData.Service.Library")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("1ca9b17f-d3f8-4fc3-a992-5135dccab9de")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/ODataService/DataSourceManager/Properties/Resources.Designer.cs b/ODataService/DataSourceManager/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..7c58e9f215
--- /dev/null
+++ b/ODataService/DataSourceManager/Properties/Resources.Designer.cs
@@ -0,0 +1,72 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Microsoft.Restier.Providers.InMemory.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Restier.Providers.InMemory.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Unable to find DataStoreManager, please add singleton of type {0} to apiBase services collection firstly. .
+ ///
+ internal static string DataStoreManagerNotFound {
+ get {
+ return ResourceManager.GetString("DataStoreManagerNotFound", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/ODataService/DataSourceManager/Properties/Resources.resx b/ODataService/DataSourceManager/Properties/Resources.resx
new file mode 100644
index 0000000000..02e299135e
--- /dev/null
+++ b/ODataService/DataSourceManager/Properties/Resources.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+ Unable to find DataStoreManager, please add singleton of type {0} to apiBase services collection firstly.
+
+
\ No newline at end of file
diff --git a/ODataService/DataSourceManager/Properties/launchSettings.json b/ODataService/DataSourceManager/Properties/launchSettings.json
new file mode 100644
index 0000000000..20bd6027ab
--- /dev/null
+++ b/ODataService/DataSourceManager/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:64492/",
+ "sslPort": 44358
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Microsoft.Restier.Providers.InMemory": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/ODataService/DataSourceManager/Submit/ChangeSetInitializer.cs b/ODataService/DataSourceManager/Submit/ChangeSetInitializer.cs
new file mode 100644
index 0000000000..e0f5121b91
--- /dev/null
+++ b/ODataService/DataSourceManager/Submit/ChangeSetInitializer.cs
@@ -0,0 +1,304 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNet.OData;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Restier.Core;
+using Microsoft.Restier.Core.Submit;
+using DataSourceManager.DataStoreManager;
+using DataSourceManager.Utils;
+
+namespace DataSourceManager.Submit
+{
+ ///
+ /// ChangeSetInitializer class.
+ /// Since our datasource is in memory,
+ /// we just confirm the data change here, not in SubmitExecutor
+ ///
+ public class ChangeSetInitializer : IChangeSetInitializer
+ {
+ public Task InitializeAsync(SubmitContext context, CancellationToken cancellationToken)
+ {
+ var requestScope = context.Api.ServiceProvider.GetService(typeof(HttpRequestScope)) as HttpRequestScope;
+ var key = InMemoryProviderUtils.GetSessionId(requestScope?.HttpRequest.HttpContext);
+ var dataStoreManager = context.GetApiService>();
+ if (dataStoreManager == null)
+ {
+ throw new ArgumentNullException("DataStoreManager Not Found",
+ typeof(IDataStoreManager).ToString());
+ }
+
+ var dataSource = dataStoreManager.GetDataStoreInstance(key);
+ foreach (var dataModificationItem in context.ChangeSet.Entries.OfType())
+ {
+ var resourceType = dataModificationItem.ExpectedResourceType;
+ if (dataModificationItem.ActualResourceType != null &&
+ dataModificationItem.ActualResourceType != dataModificationItem.ExpectedResourceType)
+ {
+ resourceType = dataModificationItem.ActualResourceType;
+ }
+
+ var operation = dataModificationItem.EntitySetOperation;
+ object resource = null;
+ switch (operation)
+ {
+ case RestierEntitySetOperation.Insert:
+ // Here we create a instance of entity, parameters are from the request.
+ // Known issues: not support odata.id
+ resource = Activator.CreateInstance(resourceType);
+ SetValues(resource, resourceType, dataModificationItem.LocalValues);
+ dataModificationItem.Resource = resource;
+
+ // insert new entity into entity set
+ var entitySetPropForInsert = GetEntitySetPropertyInfoFromDataModificationItem(dataSource,
+ dataModificationItem);
+
+ if (entitySetPropForInsert != null && entitySetPropForInsert.CanWrite)
+ {
+ var originSet = entitySetPropForInsert.GetValue(dataSource);
+ entitySetPropForInsert.PropertyType.GetMethod("Add").Invoke(originSet, new[] {resource});
+ }
+ break;
+ case RestierEntitySetOperation.Update:
+ resource = FindResource(dataSource, context, dataModificationItem, cancellationToken);
+ dataModificationItem.Resource = resource;
+
+ // update the entity
+ if (resource != null)
+ {
+ SetValues(resource, resourceType, dataModificationItem.LocalValues);
+ }
+ break;
+ case RestierEntitySetOperation.Delete:
+ resource = FindResource(dataSource, context, dataModificationItem, cancellationToken);
+ dataModificationItem.Resource = resource;
+
+ // remove the entity
+ if (resource != null)
+ {
+ var entitySetPropForRemove = GetEntitySetPropertyInfoFromDataModificationItem(dataSource,
+ dataModificationItem);
+
+ if (entitySetPropForRemove != null && entitySetPropForRemove.CanWrite)
+ {
+ var originSet = entitySetPropForRemove.GetValue(dataSource);
+ entitySetPropForRemove.PropertyType.GetMethod("Remove").Invoke(originSet, new[] {resource});
+ }
+ }
+ break;
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ return Task.WhenAll();
+ }
+
+ ///
+ /// Convert EdmStructuredObject to an object with type.
+ ///
+ /// An object with EdmStructuredType.
+ /// Desired object type.
+ /// Result object.
+ private static object ConvertEdmStructuredObjectToTypedObject(
+ IEdmStructuredObject edmStructuredObject, Type type)
+ {
+ if (edmStructuredObject == null)
+ {
+ return null;
+ }
+
+ var obj = Activator.CreateInstance(type);
+ var propertyInfos = type.GetProperties();
+ foreach (var propertyInfo in propertyInfos)
+ {
+ if (!propertyInfo.CanWrite)
+ {
+ continue;
+ }
+
+ object value = null;
+ edmStructuredObject.TryGetPropertyValue(propertyInfo.Name, out value);
+ if (value == null)
+ {
+ propertyInfo.SetValue(obj, value);
+ continue;
+ }
+
+ if (!propertyInfo.PropertyType.IsInstanceOfType(value))
+ {
+ var edmObj = value as IEdmStructuredObject;
+ if (edmObj == null)
+ {
+ throw new NotSupportedException(string.Format(
+ CultureInfo.InvariantCulture,
+ propertyInfo.PropertyType.ToString()));
+ }
+
+ value = ConvertEdmStructuredObjectToTypedObject(edmObj, propertyInfo.PropertyType);
+ }
+
+ propertyInfo.SetValue(obj, value);
+ }
+
+ return obj;
+ }
+
+ ///
+ /// Convert EdmStructuredObjectCollection object to an object with type.
+ ///
+ /// EdmStructuredObjectCollection type.
+ /// An object with EdmStructuredObjectCollection.
+ /// Desired object type.
+ /// Result object.
+ private static object ConvertEdmStructuredObjectCollectionToTypedObject(
+ TEdmStructuredObjectCollection edmStructuredObjectCollection, Type type)
+ {
+ var valueType = typeof(Collection<>).MakeGenericType(type);
+ var value = Activator.CreateInstance(valueType);
+ var collection = edmStructuredObjectCollection as System.Collections.IEnumerable;
+ if (collection == null)
+ {
+ return null;
+ }
+
+ foreach (var c in collection)
+ {
+ var obj = ConvertEdmStructuredObjectToTypedObject(c as IEdmStructuredObject, type);
+ value.GetType().GetMethod("Add").Invoke(value, new[] {obj});
+ }
+
+ return value;
+ }
+
+ private static void SetValues(object instance, Type type, IReadOnlyDictionary values)
+ {
+ foreach (var propertyPair in values)
+ {
+ var value = propertyPair.Value;
+ var propertyInfo = type.GetProperty(propertyPair.Key);
+ if (propertyInfo == null)
+ {
+ continue;
+ }
+
+ if (value == null)
+ {
+ // If the property value is null, we set null in the object too.
+ propertyInfo.SetValue(instance, null);
+ continue;
+ }
+
+ if (!propertyInfo.PropertyType.IsInstanceOfType(value))
+ {
+ var dic = value as IReadOnlyDictionary;
+
+ if (dic != null)
+ {
+ value = propertyInfo.GetValue(instance);
+ SetValues(value, propertyInfo.PropertyType, dic);
+ }
+ else if (propertyInfo.PropertyType.IsGenericType)
+ {
+ // EdmStructuredObjectCollection
+ var realType = propertyInfo.PropertyType.GenericTypeArguments[0];
+ value = ConvertEdmStructuredObjectCollectionToTypedObject(value, realType);
+ }
+ else
+ {
+ throw new NotSupportedException(string.Format(
+ CultureInfo.InvariantCulture,
+ propertyPair.Key));
+ }
+ }
+
+ propertyInfo.SetValue(instance, value);
+ }
+ }
+
+ private static object FindResource(
+ object instance,
+ SubmitContext context,
+ DataModificationItem item,
+ CancellationToken cancellationToken)
+ {
+ var entitySetPropertyInfo = GetEntitySetPropertyInfoFromDataModificationItem(instance, item);
+ var originSet = entitySetPropertyInfo.GetValue(instance);
+ object resource = null;
+ Type resourceType = null;
+ var enumerableSet = originSet as IEnumerable