Skip to content

Commit

Permalink
Make model auto pop keys from Entity framework.
Browse files Browse the repository at this point in the history
  • Loading branch information
chinadragon0515 committed Jun 28, 2016
1 parent e605349 commit c542db6
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 158 deletions.
5 changes: 3 additions & 2 deletions src/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
#region CA1006 Nested Generic Type
[assembly: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.Restier.Core.ApiBaseExtensions.#QueryAsync`1(Microsoft.Restier.Core.ApiBase,System.Linq.IQueryable`1<!!0>,System.Threading.CancellationToken)")]
[assembly: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.Restier.Core.ApiBaseExtensions.#QueryAsync`2(Microsoft.Restier.Core.ApiBase,System.Linq.IQueryable`1<!!0>,System.Linq.Expressions.Expression`1<System.Func`2<System.Linq.IQueryable`1<!!0>,!!1>>,System.Threading.CancellationToken)")]
[assembly: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.Restier.Core.Model.ModelContext.#EntityTypeKeyPropertiesMapDictionary")]
[assembly: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.Restier.Core.Query.QueryRequest.#Create`2(System.Linq.IQueryable`1<!!0>,System.Linq.Expressions.Expression`1<System.Func`2<System.Linq.IQueryable`1<!!0>,!!1>>,System.Nullable`1<System.Boolean>)")]
[assembly: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.Restier.Core.Model.ModelContext.#EntitySetTypeMapCollection")]
#endregion

#region CA1020 Few types in namespace
Expand Down Expand Up @@ -149,7 +149,8 @@
[assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Microsoft.Restier.Core.ApiContext.#.ctor(Microsoft.Restier.Core.ApiConfiguration)")]
[assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Microsoft.Restier.Core.QueryableSource.#System.Linq.IQueryProvider.CreateQuery`1(System.Linq.Expressions.Expression)")]
[assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Microsoft.Restier.Core.QueryableSource.#System.Linq.IQueryProvider.CreateQuery(System.Linq.Expressions.Expression)")]
[assembly: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "Microsoft.Restier.Core.Model.ModelContext.#EntitySetTypeMapCollection")]
[assembly: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "Microsoft.Restier.Core.Model.ModelContext.#EntitySetTypeMapDictionary")]
[assembly: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "Microsoft.Restier.Core.Model.ModelContext.#EntityTypeKeyPropertiesMapDictionary")]
#endregion

#region CA1801 Unused Parameters
Expand Down
14 changes: 11 additions & 3 deletions src/Microsoft.Restier.Core/Model/ModelContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;

namespace Microsoft.Restier.Core.Model
{
Expand All @@ -24,8 +24,16 @@ public ModelContext(ApiContext apiContext)
}

/// <summary>
/// Gets or sets Entity set and entity type map collection, it will be used by publisher for model build.
/// Gets or sets Entity set and entity type map dictionary, it will be used by publisher for model build.
/// </summary>
public Collection<KeyValuePair<string, Type>> EntitySetTypeMapCollection { get; set; }
public IDictionary<string, Type> EntitySetTypeMapDictionary { get; set; }

/// <summary>
/// Gets or sets entity type and its key properties map dictionary, and used by publisher for model build.
/// This is useful when key properties does not have key attribute
/// or follow Web Api OData key property naming convention.
/// Otherwise, this collection is not needed.
/// </summary>
public IDictionary<Type, ICollection<PropertyInfo>> EntityTypeKeyPropertiesMapDictionary { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#if !EF7
using System.Data.Entity;
#endif
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -42,43 +45,35 @@ public Task<IEdmModel> GetModelAsync(ModelContext context, CancellationToken can
{
Ensure.NotNull(context, "context");

var entitySetTypeMapCollection = new Collection<KeyValuePair<string, Type>>();
var apiContext = context.ApiContext;
var dbContext = apiContext.GetApiService<DbContext>();
var entitySetTypeMapDictionary = new Dictionary<string, Type>();
var entityTypeKeyPropertiesMapDictionary = new Dictionary<Type, ICollection<PropertyInfo>>();
var dbContext = context.ApiContext.GetApiService<DbContext>();

List<PropertyInfo> props = GetDbSetProperties(dbContext);
foreach (var prop in props)
{
var type = prop.PropertyType.GenericTypeArguments[0];
var pair = new KeyValuePair<string, Type>(prop.Name, type);
entitySetTypeMapCollection.Add(pair);
}

context.EntitySetTypeMapCollection = entitySetTypeMapCollection;
return Task.FromResult<IEdmModel>(null);
}

internal static List<PropertyInfo> GetDbSetProperties(DbContext dbContext)
{
var dbSetProperties = new List<PropertyInfo>();
var properties = dbContext.GetType().GetProperties();
var efModel = (dbContext as IObjectContextAdapter).ObjectContext.MetadataWorkspace;
var efEntityContainer = efModel.GetItems<EntityContainer>(DataSpace.CSpace).Single();
var itemCollection = (ObjectItemCollection)efModel.GetItemCollection(DataSpace.OSpace);

foreach (var property in properties)
foreach (var efEntitySet in efEntityContainer.EntitySets)
{
var type = property.PropertyType;
#if EF7
var genericType = type.FindGenericType(typeof(DbSet<>));
#else
var genericType = type.FindGenericType(typeof(IDbSet<>));
#endif
var efEntityType = efEntitySet.ElementType;
var objectSpaceType = efModel.GetObjectSpaceType(efEntityType);
Type clrType = itemCollection.GetClrType(objectSpaceType);

if (genericType != null)
// As entity set name and type map
entitySetTypeMapDictionary.Add(efEntitySet.Name, clrType);

ICollection<PropertyInfo> keyProperties = new List<PropertyInfo>();
foreach (var property in efEntityType.KeyProperties)
{
dbSetProperties.Add(property);
keyProperties.Add(clrType.GetProperty(property.Name));
}

entityTypeKeyPropertiesMapDictionary.Add(clrType, keyProperties);
}

return dbSetProperties;
context.EntitySetTypeMapDictionary = entitySetTypeMapDictionary;
context.EntityTypeKeyPropertiesMapDictionary = entityTypeKeyPropertiesMapDictionary;
return Task.FromResult<IEdmModel>(null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public async Task<IEdmModel> GetModelAsync(ModelContext context, CancellationTok
}
}

var collection = context.EntitySetTypeMapCollection;
if (collection == null || collection.Count == 0)
var entitySetTypeMapDictionary = context.EntitySetTypeMapDictionary;
if (entitySetTypeMapDictionary == null || entitySetTypeMapDictionary.Count == 0)
{
return null;
}
Expand All @@ -41,7 +41,7 @@ public async Task<IEdmModel> GetModelAsync(ModelContext context, CancellationTok
MethodInfo method = typeof(ODataConventionModelBuilder)
.GetMethod("EntitySet", BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);

foreach (var pair in collection)
foreach (var pair in entitySetTypeMapDictionary)
{
// Build a method with the specific type argument
var specifiedMethod = method.MakeGenericMethod(pair.Value);
Expand All @@ -53,7 +53,27 @@ public async Task<IEdmModel> GetModelAsync(ModelContext context, CancellationTok
specifiedMethod.Invoke(builder, parameters);
}

context.EntitySetTypeMapCollection.Clear();
entitySetTypeMapDictionary.Clear();

var entityTypeKeyPropertiesMapDictionary = context.EntityTypeKeyPropertiesMapDictionary;
if (entityTypeKeyPropertiesMapDictionary != null)
{
foreach (var pair in entityTypeKeyPropertiesMapDictionary)
{
var edmTypeConfiguration = builder.GetTypeConfigurationOrNull(pair.Key) as EntityTypeConfiguration;
if (edmTypeConfiguration == null)
{
continue;
}

foreach (var property in pair.Value)
{
edmTypeConfiguration.HasKey(property);
}
}

entityTypeKeyPropertiesMapDictionary.Clear();
}

return builder.GetEdmModel();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="EntityFramework">
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.0.0-rc2-final\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
Expand Down
13 changes: 13 additions & 0 deletions test/Microsoft.Restier.Publishers.OData.Test/app.config
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
</configSections>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
Expand All @@ -16,4 +20,13 @@
</dependentAssembly>
</assemblyBinding>
</runtime>
<entityFramework>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
</entityFramework>
<connectionStrings>
<add name="LibraryContext" connectionString="data source=(localdb)\MSSQLLocalDB;initial catalog=LibraryAPIDB;integrated security=True;connect timeout=30;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient"/>
</connectionStrings>
</configuration>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EntityFramework" version="6.1.3" targetFramework="net45" />
<package id="Microsoft.AspNet.OData" version="5.9.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45" />
Expand Down
3 changes: 2 additions & 1 deletion test/Microsoft.Restier.TestCommon/PublicApi.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,8 @@ public interface Microsoft.Restier.Core.Model.IModelMapper {
public class Microsoft.Restier.Core.Model.ModelContext : Microsoft.Restier.Core.InvocationContext {
public ModelContext (Microsoft.Restier.Core.ApiContext apiContext)

System.Collections.ObjectModel.Collection`1[[System.Collections.Generic.KeyValuePair`2[[System.String],[System.Type]]]] EntitySetTypeMapCollection { [CompilerGeneratedAttribute(),]public get; [CompilerGeneratedAttribute(),]public set; }
System.Collections.Generic.IDictionary`2[[System.String],[System.Type]] EntitySetTypeMapDictionary { [CompilerGeneratedAttribute(),]public get; [CompilerGeneratedAttribute(),]public set; }
System.Collections.Generic.IDictionary`2[[System.Type],[System.Collections.Generic.ICollection`1[[System.Reflection.PropertyInfo]]]] EntityTypeKeyPropertiesMapDictionary { [CompilerGeneratedAttribute(),]public get; [CompilerGeneratedAttribute(),]public set; }
}

public interface Microsoft.Restier.Core.Query.IQueryExecutor {
Expand Down
Loading

0 comments on commit c542db6

Please sign in to comment.