From 729fc83325ba4e990170f2decfcc9dec77fffc81 Mon Sep 17 00:00:00 2001 From: "Robert McLaws (Microsoft MVP)" Date: Wed, 28 Sep 2016 09:22:33 -0400 Subject: [PATCH 1/7] Fix ModelProducer to handle multiple APIs with different DbContexts Fixes #527. --- .../Model/ModelProducer.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs b/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs index b67228b9..8f03deb6 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs +++ b/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs @@ -62,7 +62,19 @@ public Task GetModelAsync(ModelContext context, CancellationToken can var dbContext = context.GetApiService(); var efModel = (dbContext as IObjectContextAdapter).ObjectContext.MetadataWorkspace; - var efEntityContainer = efModel.GetItems(DataSpace.CSpace).Single(); + var efEntityContainers = efModel.GetItems(DataSpace.CSpace); + var efEntityContainer = efEntityContainers.FirstOrDefault(c => c.Name == dbContext.GetType().Name); + if (efEntityContainer == null) + { + if (efEntityContainers.Count > 1) + { + var containerNames = efEntityContainers.Aggregate("", (current, next) => next.Name + ", "); + throw new Exception("This project has multiple EntityFrameworkApis using different DbContexts, and the correct contect could not be loaded. \r\n" + + $"The contexts available are '{containerNames.Substring(0, containerNames.Length - 2)}' but the Container expects '{efEntityContainer.Name}'."); + } + throw new Exception("Could not find the correct DbContext instance for this EntityFrameworkApi. \r\n" + + $"The Context name was '{dbContext.GetType().Name}' but the Container expects '{efEntityContainer.Name}'."); + } var itemCollection = (ObjectItemCollection)efModel.GetItemCollection(DataSpace.OSpace); foreach (var efEntitySet in efEntityContainer.EntitySets) From ab3a2950a2333b41cec8176a3aed0b1a6f1dca29 Mon Sep 17 00:00:00 2001 From: "Robert McLaws (Microsoft MVP)" Date: Wed, 28 Sep 2016 13:57:54 -0400 Subject: [PATCH 2/7] Spelling fix --- .../Model/ModelProducer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs b/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs index 8f03deb6..7be8d609 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs +++ b/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs @@ -69,7 +69,7 @@ public Task GetModelAsync(ModelContext context, CancellationToken can if (efEntityContainers.Count > 1) { var containerNames = efEntityContainers.Aggregate("", (current, next) => next.Name + ", "); - throw new Exception("This project has multiple EntityFrameworkApis using different DbContexts, and the correct contect could not be loaded. \r\n" + + throw new Exception("This project has multiple EntityFrameworkApis using different DbContexts, and the correct context could not be loaded. \r\n" + $"The contexts available are '{containerNames.Substring(0, containerNames.Length - 2)}' but the Container expects '{efEntityContainer.Name}'."); } throw new Exception("Could not find the correct DbContext instance for this EntityFrameworkApi. \r\n" + From af93756f1b2c478f8e03b48e51d0d893a604d7ce Mon Sep 17 00:00:00 2001 From: "Robert McLaws (Microsoft MVP)" Date: Fri, 30 Sep 2016 13:50:40 -0400 Subject: [PATCH 3/7] Explanatory comments for the Multiple Contexts fix Added explanations for: - why the bug is happening. - how we work around it that attempts to ensure it keeps working in the future. - how we are informing the user in detail about any problems they might encounter, and how to solve them. --- .../Model/ModelProducer.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs b/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs index 7be8d609..be592cfa 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs +++ b/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs @@ -62,16 +62,26 @@ public Task GetModelAsync(ModelContext context, CancellationToken can var dbContext = context.GetApiService(); var efModel = (dbContext as IObjectContextAdapter).ObjectContext.MetadataWorkspace; + // @robertmclaws: The query below actually returns all registered Containers across all registered DbContexts. + // It is likely a bug in some other part of OData. But we can roll with it. var efEntityContainers = efModel.GetItems(DataSpace.CSpace); + // @robertmclaws: Because of the bug above, we should not make any assumptions about what is returned, and get + // the specific container we want to use. Even if the bug gets fixed, the next line should still + // continue to work. var efEntityContainer = efEntityContainers.FirstOrDefault(c => c.Name == dbContext.GetType().Name); + // @robertmclaws: Now that we're doing a proper FirstOrDefault() instead of a Single(), we wont' crash if more + // that one is returned, and we can check for null and inform the user specifically what happened. if (efEntityContainer == null) { if (efEntityContainers.Count > 1) { + // @robertmclaws: In this case, we have multiple DbContexts available, but none of them match up. + // Tell the user what we have, and what we were expecting, so they can fix it. var containerNames = efEntityContainers.Aggregate("", (current, next) => next.Name + ", "); throw new Exception("This project has multiple EntityFrameworkApis using different DbContexts, and the correct context could not be loaded. \r\n" + $"The contexts available are '{containerNames.Substring(0, containerNames.Length - 2)}' but the Container expects '{efEntityContainer.Name}'."); } + // @robertmclaws: In this case, we only had one DbContext available, and if wasn't thw right one. throw new Exception("Could not find the correct DbContext instance for this EntityFrameworkApi. \r\n" + $"The Context name was '{dbContext.GetType().Name}' but the Container expects '{efEntityContainer.Name}'."); } From 4c0015ba5bd5e82372be10137b5e00e16cb99d54 Mon Sep 17 00:00:00 2001 From: "Robert McLaws (Microsoft MVP)" Date: Wed, 12 Oct 2016 15:04:38 -0400 Subject: [PATCH 4/7] Spelling fix --- .../Model/ModelProducer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs b/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs index be592cfa..b956d8bf 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs +++ b/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs @@ -70,7 +70,7 @@ public Task GetModelAsync(ModelContext context, CancellationToken can // continue to work. var efEntityContainer = efEntityContainers.FirstOrDefault(c => c.Name == dbContext.GetType().Name); // @robertmclaws: Now that we're doing a proper FirstOrDefault() instead of a Single(), we wont' crash if more - // that one is returned, and we can check for null and inform the user specifically what happened. + // than one is returned, and we can check for null and inform the user specifically what happened. if (efEntityContainer == null) { if (efEntityContainers.Count > 1) From 35ff71c316b1a848b9b27ba4b74c913559923bab Mon Sep 17 00:00:00 2001 From: "Robert McLaws (Microsoft MVP)" Date: Sat, 24 Dec 2016 14:31:27 -0500 Subject: [PATCH 5/7] Use Resources for Exception --- .../Model/ModelProducer.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs b/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs index b956d8bf..3e59ff4d 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs +++ b/src/Microsoft.Restier.Providers.EntityFramework/Model/ModelProducer.cs @@ -78,12 +78,10 @@ public Task GetModelAsync(ModelContext context, CancellationToken can // @robertmclaws: In this case, we have multiple DbContexts available, but none of them match up. // Tell the user what we have, and what we were expecting, so they can fix it. var containerNames = efEntityContainers.Aggregate("", (current, next) => next.Name + ", "); - throw new Exception("This project has multiple EntityFrameworkApis using different DbContexts, and the correct context could not be loaded. \r\n" + - $"The contexts available are '{containerNames.Substring(0, containerNames.Length - 2)}' but the Container expects '{efEntityContainer.Name}'."); + throw new Exception(string.Format(Resources.MultipleDbContextsExpectedException, containerNames.Substring(0, containerNames.Length - 2), efEntityContainer.Name)); } // @robertmclaws: In this case, we only had one DbContext available, and if wasn't thw right one. - throw new Exception("Could not find the correct DbContext instance for this EntityFrameworkApi. \r\n" + - $"The Context name was '{dbContext.GetType().Name}' but the Container expects '{efEntityContainer.Name}'."); + throw new Exception(string.Format(Resources.DbContextCouldNotBeFoundException, dbContext.GetType().Name, efEntityContainer.Name)); } var itemCollection = (ObjectItemCollection)efModel.GetItemCollection(DataSpace.OSpace); From 762fb8b9c925f39dbbc2ff18c7786e5bf3fd5d45 Mon Sep 17 00:00:00 2001 From: "Robert McLaws (Microsoft MVP)" Date: Sat, 24 Dec 2016 14:34:33 -0500 Subject: [PATCH 6/7] =?UTF-8?q?Updating=C2=A0Resources,=20Part=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Properties/Resources.Designer.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.Designer.cs b/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.Designer.cs index d05c1dc1..39fc17eb 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.Designer.cs +++ b/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -69,6 +69,15 @@ internal static string DataModificationMustBeCUD { } } + /// + /// Looks up a localized string similar to Could not find the correct DbContext instance for this EntityFrameworkApi. \r\n The Context name was '{0}' but the Container expects '{1}'.. + /// + internal static string DbContextCouldNotBeFoundException { + get { + return ResourceManager.GetString("DbContextCouldNotBeFoundException", resourceCulture); + } + } + /// /// Looks up a localized string similar to Need 'LineString type', while input is {0}.. /// @@ -87,6 +96,15 @@ internal static string InvalidPointGeographyType { } } + /// + /// Looks up a localized string similar to This project has multiple EntityFrameworkApis using different DbContexts, and the correct context could not be loaded. \r\n The contexts available are '{0}' but the Container expects '{1}'.. + /// + internal static string MultipleDbContextsExpectedException { + get { + return ResourceManager.GetString("MultipleDbContextsExpectedException", resourceCulture); + } + } + /// /// Looks up a localized string similar to The precondition check for request {0} on resource {1} is failed.. /// From d1e5944e83eefd51ef161656d59052012e8ec75d Mon Sep 17 00:00:00 2001 From: "Robert McLaws (Microsoft MVP)" Date: Sat, 24 Dec 2016 14:35:33 -0500 Subject: [PATCH 7/7] Updating Resources, Part 2 of 2 --- .../Properties/Resources.resx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.resx b/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.resx index dd1d9dd8..e328fed3 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.resx +++ b/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.resx @@ -1,4 +1,4 @@ - +