问题
I used to access the data context of my (ef 5.0) entities from inside a wcf data services service operation with this.CurrentDataSource.MyEntity
.
My data service inherited from DataService<T>
. Now I wanted to use entity framework 6.0 and read on the internet, I should inherit the service from EntityFrameworkDataService<T>
. But now from inside my service operations, I cannot access my data context anymore. this.CurrentDataSource
doesn't contain any reference to the entities.
回答1:
Here's my workaround using an extension method that gets the underlying data model via (cached) reflection info. It works for the current Microsoft.OData.EntityFrameworkProvider version 1.0.0 alpha 2.
Sample usage is for a custom WebGet method:
[WebGet]
public string GetMyEntityName(string myEntityKey)
{
var model = this.CurrentDataSource.GetDataModel();
var entity = model.MyEntity.Find(myEntityKey);
return entity.Name;
}
And implementation:
public static class EntityFrameworkDataServiceProvider2Extensions
{
/// <summary>
/// Gets the underlying data model currently used by an EntityFrameworkDataServiceProvider2.
/// </summary>
/// <remarks>
/// TODO: Obsolete this method if the API changes to support access to the model.
/// Reflection is used as a workaround because EntityFrameworkDataServiceProvider2 doesn't (yet) provide access to its underlying data source.
/// </remarks>
public static T GetDataModel<T>(this EntityFrameworkDataServiceProvider2<T> efProvider) where T : class
{
if (efProvider != null)
{
Type modelType = typeof(T);
// Get the innerProvider field info for an EntityFrameworkDataServiceProvider2 of the requested type
FieldInfo ipField;
if (!InnerProviderFieldInfoCache.TryGetValue(modelType, out ipField))
{
ipField = efProvider.GetType().GetField("innerProvider", BindingFlags.NonPublic | BindingFlags.Instance);
InnerProviderFieldInfoCache.Add(modelType, ipField);
}
var innerProvider = ipField.GetValue(efProvider);
if (innerProvider != null)
{
// Get the CurrentDataSource property of the innerProvider
PropertyInfo cdsProperty;
if (!CurrentDataSourcePropertyInfoCache.TryGetValue(modelType, out cdsProperty))
{
cdsProperty = innerProvider.GetType().GetProperty("CurrentDataSource");
CurrentDataSourcePropertyInfoCache.Add(modelType, cdsProperty);
}
return cdsProperty.GetValue(innerProvider, null) as T;
}
}
return null;
}
private static readonly ConditionalWeakTable<Type, FieldInfo> InnerProviderFieldInfoCache = new ConditionalWeakTable<Type, FieldInfo>();
private static readonly ConditionalWeakTable<Type, PropertyInfo> CurrentDataSourcePropertyInfoCache = new ConditionalWeakTable<Type, PropertyInfo>();
}
System.Runtime.CompilerServices.ConditionalWeakTable was used to cache reflection results based on a suggestion taken from Caching reflection data
回答2:
It worked! Here is the code converted in VB.NET
Imports System.Reflection
Imports System.Runtime.CompilerServices
Imports System.Data.Services.Providers
Public Module EntityFrameworkDataServiceProvider2Extensions
''' <summary>
''' Gets the underlying data model currently used by an EntityFrameworkDataServiceProvider2.
''' </summary>
''' <remarks>
''' TODO: Obsolete this method if the API changes to support access to the model.
''' Reflection is used as a workaround because EntityFrameworkDataServiceProvider2 doesn't (yet) provide access to its underlying data source.
''' </remarks>
<System.Runtime.CompilerServices.Extension> _
Public Function GetDataModel(Of T As Class)(efProvider As EntityFrameworkDataServiceProvider2(Of T)) As T
If efProvider IsNot Nothing Then
Dim modelType As Type = GetType(T)
' Get the innerProvider field info for an EntityFrameworkDataServiceProvider2 of the requested type
Dim ipField As FieldInfo = Nothing
If Not InnerProviderFieldInfoCache.TryGetValue(modelType, ipField) Then
ipField = efProvider.[GetType]().GetField("innerProvider", BindingFlags.NonPublic Or BindingFlags.Instance)
InnerProviderFieldInfoCache.Add(modelType, ipField)
End If
Dim innerProvider = ipField.GetValue(efProvider)
If innerProvider IsNot Nothing Then
' Get the CurrentDataSource property of the innerProvider
Dim cdsProperty As PropertyInfo = Nothing
If Not CurrentDataSourcePropertyInfoCache.TryGetValue(modelType, cdsProperty) Then
cdsProperty = innerProvider.[GetType]().GetProperty("CurrentDataSource")
CurrentDataSourcePropertyInfoCache.Add(modelType, cdsProperty)
End If
Return TryCast(cdsProperty.GetValue(innerProvider, Nothing), T)
End If
End If
Return Nothing
End Function
Private ReadOnly InnerProviderFieldInfoCache As New ConditionalWeakTable(Of Type, FieldInfo)()
Private ReadOnly CurrentDataSourcePropertyInfoCache As New ConditionalWeakTable(Of Type, PropertyInfo)()
End Module
回答3:
It seems that the EntitiyFrameworkDataService
doesn't provide access to the underlying DataService
and its current datasource, as stated here and here.
So the only way seems to change each service operation to create a new data context each time.
来源:https://stackoverflow.com/questions/19953784/entity-framework-6-data-context-from-wcf-data-service