背景背景:
我正在開發一個資料庫專案,該專案從決議的資料集構建模型,然后使用物體和物體框架擴展(用于批量操作)將這些模型與資料庫合并。物體框架擴展允許覆寫執行合并/插入/等時使用的主鍵,方法是提供一個指定要使用的匿名型別的委托,其中匿名型別僅具有用于主鍵的屬性。
例子:
context.BulkMerge<T>(IEnumerable<T>,options => options.ColumnPrimaryKeyExpression = x => new { x.Id, x.Name });
我有一個函式Expression<Func<T, object>> GenerateAnonTypeExpression<T>(this IEnumerable<PropertyInfo> fields)用于生成這個匿名型別 lambda Expression<Func<T, object>>,我可以將它作為列主鍵運算式傳遞給 BulkMerge,如下所示:
void BulkMergeCollection<T>(IEnumerable<T> entities)
{
IEnumerable<PropertyInfo> keyProperties = entites.GetKeyProperties() //Returns a list of PropertyInfos from type T to use as the primary key for entites of type T
var KeyPropertiesExpression = keyProperties.GenerateAnonTypeExpression<T>()
using (var ctx = new Context())
{
ctx.BulkMerge(entities, options =>
{
options.ColumnPrimaryKeyExpression = keyPropertiesExpression;
});
}
}
這可以正常作業,沒有任何問題。但是,這僅適用于單個表的物體并且不合并子物體,這導致必須BulkMergeCollection()逐個型別呼叫所有物體和所有子物體(目前通常大約 12-13 次呼叫) . 雖然這通常是可行的,但我已經開始在 Database First 背景關系而不是 Code First 環境中作業,這意味著 .edmx 中不存在橋表作為可以實體化的模型,而是僅作為左右物體的子物體存在. 這意味著當以這種方式使用 BulkMerge 時,我的橋接表不會被填充(即使左右物體具有子物體的值)。
幸運的是,Entity Framework Extensions 還有一個 Include Graph 選項,允許在呼叫BulkMerge(). 這要求為所有正在合并的型別指定列主鍵選項。
具有 Many:Many 關系的 Person 和 Address 模型示例
public class Person
{
public Person()
{
this.Addresses = new HashSet<Address>();
}
public int Id { get; set; } //Identity column assigned by the database
public string FirstName { get; set; }
public string LastName { get; set; }
public string SSN { get; set; } //Unique identifier.
public virtual ICollection<Address> Addresses { get; set; }
}
public class Address
{
public Address()
{
this.Inhabitants = new HashSet<Person>();
}
public int Id { get; set; } //Identity column assigned by the database
public int HouseNumber { get; set; } //Unique Identifier with StreetName and Zip
public string StreetName { get; set; } //Unique Identifier with HouseNumber and Zip
public string Unit { get; set; }
public int Zipcode { get; set; } //Unique Identifier with StreetName and HouseNumber
public virtual ICollection<Person> Inhabitants { get; set; }
}
public void TestFunction()
{
Address home = new Address()
{
HouseNumber = 1234;
StreetName = "1st St";
Zipcode = 99999;
}
Person john = new Person()
{
FirstName = "John";
LastName = "Smith";
SSN = 123456789;
}
john.Addresses.Add(home);
IEnumerable<Person> persons = new List<Person>() { john };
BulkMergePersonsAndAddresses(persons);
}
public void BulkMergePersonsAndAddresses(IEnumerable<Person> persons)
{
using (var ctx = new Context())
{
BulkMerge(persons, options =>
{
options.IncludeGraph = true;
options.IncludeGraphOperationBuilder = operation =>
{
if(operation is BulkOperation<Person>)
{
var bulk = (BulkOperation<Person>)operation;
bulk.ColumnPrimaryKeyExpression = x => new { x.SSN };
}
else if(operation is BulkOperation<Address>)
{
var bulk = (BulkOperation<Address>)operation;
bulk.ColumnPrimaryKeyExpression = x => new
{
x.HouseNumber,
x.StreetName,
x.Zipcode
};
}
}
}
}
}
我已經用硬編碼的操作(如上)和bulk.ColumnPrimaryKeyExpression由GenerateAnonTypeExpression<T>();生成的單個 s 來測驗了這一點。這兩種方法都可以正常作業并成功地使用橋接表添加/合并專案。
問題:
我要做的是將選項的整個主體構建IncludeGraphOperationBuilder為 Lambda 運算式樹(類似于我處理列主鍵運算式的方式)。這是必要的,因為根據要合并的基本模型型別IncludeGraphOperationBuilder將有不同的部分。BulkOperation<...>此外,我希望IncludeGraphOperationBuilder動態生成的主體,這樣我就不需要在每次支持資料庫模型更改時添加新部分。我的問題是,當嘗試為給定if(operation is BulkOperation<T>)塊的主體生成運算式時,當我嘗試將由該GenerateAnonTypeExpression<T>()方法創建的 Lambda 運算式分配給表示 的成員運算式時bulk.ColumnPrimaryKeyExpression,我得到一個引數例外
型別的運算式
System.Func`2[T, Object]不能用于賦值給型別System.Linq.Expressions.Expression`1[System.Func`2[T, Object]]
我不知道這是如何發生的,因為回傳的GenerateAnonTypeExpression<T>()明確是 typeExpression<Func<T, object>>而不是Func<T, object>,所以我不明白Func<T, object>試圖在分配中使用的 來自哪里。
以下是發生故障的完整代碼: 請注意,IModelItem 是一個介面,允許通過反射從模型中檢索唯一標識屬性
public static void GenerateAnonTypeGraphExpression<T>(this IEnumerable<T> models)
where T : class, IModelItem
{
Expression<Func<T, object> keyProperties = models.GetUniqueIdProperties().GenerateAnonTypeExpression<T>();
ParameterExpression bulkVar = Expression.Variable(typeof(BulkOperaton<T>), "bulk"); //Creates the variable "var bulk"
ParameterExpression operation = Expression.Parameter(typeof(BulkOperation), "operation");
var castExpression = Expression.Convert(operation, typeof(BulkOperation<T>)); //"(BulkOperation<T>) operation"
var bulkLineExpression = Expression.Assign(bulkVar, castExpression); //"var bulk = (BulkOperation<T>) operation"
var columnPrimaryKeyProperty = typeof(BulkOperation<T>).GetProperties().Where(p => p.Name == "ColumnPrimaryKeyExpression").FirstOrDefault();
var colmunPrimaryKeyPropertyExpression = Expression.Property(bulkVar, columnPrimaryKeyProperty); //"bulk.ColumnPrimaryKeyExpression"
//The next line is where it blows up with the above Argument Exception
var colmunPrimaryKeyPropertyAssignmentExpression = Expression.Assign(columnPrimaryKeyPropertyExpression, keyProperties); //"bulk.ColumnPrimayKeyExpression = keyProperties"
var bodyExpression = Expression.Block(
bulkLineExpression,
columnPrimaryKeyPropertyAssignmentExpression
);
}
我嘗試過的所有操作都會導致相同的錯誤,并且我無法在網上找到有關該特定錯誤的任何檔案。在除錯時單步執行代碼表明它keyProperties是失敗行之前的型別Expression<Func<T, object>>并將其強制轉換到該行不會改變結果。using 也不會Expression.Member代替Expression.Property.
我在這里有點茫然,顯然對運算式的理解不夠好。有人可以解釋我做錯了什么嗎?
uj5u.com熱心網友回復:
回答未來的讀者和我自己的參考。答案來自@Svayatoslav Danyliv 對原始問題的評論。
訣竅是將原始回傳的 Lambda 運算式 ( keyProperties) 包裝在一個Expression.Constant()呼叫中,它告訴系統按原樣使用原始 Lambda(一個運算式),而不是試圖將其解釋為賦值的一部分。
將失敗的行更新為:
var colmunPrimaryKeyPropertyAssignmentExpression =
Expression.Assign(columnPrimaryKeyPropertyExpression, Expression.Constant(keyProperties));
分配按預期作業,不再引發例外。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/517094.html
