我正在尝试更新给定客户ID,行ID和动态列名称的记录。
到目前为止,我有以下内容,故障点标记为***:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id && x.RVals.Id == rId && x.***column?*** == column).First();
entry = value;
}
我无法找到如何做到这一点的好例子。
答案 0 :(得分:2)
最后评论后添加
您无法找到示例的原因是因为它不是一个好的设计。
您的方法非常容易出错,难以测试且难以维护。如果有人输入错误的列名怎么办?如果您尝试将字符串分配给客户的生日怎么办?即使您要对列名和建议值执行一些字符串检查,在有人更改列的名称或类型后,您的程序也不再有效。
让我们重新设计!
显然,您有一个Customer
Id
和一个属性Rvals
。此属性Rvals
也有一个属性Id
。
您还有一个函数GetRValId
,可以将字符串rval
转换为int rvalId
。
您想要的是Id
和字符串rval
,您希望使用此Id
和rValId
更新第一个客户的其中一列。
附带问题:ID可以有多个客户吗?在这种情况下:你确定Id是一个ID吗?如果有更多匹配的客户,您想要什么?更新所有客户或仅更新第一个客户?您将哪个客户定义为第一个客户?
抛开旁边的问题。我们想要一个函数签名,如果您使用不存在的客户属性,或者如果您尝试将字符串分配给生日,则在编译时报告错误。这样的事可能呢?
更新客户名称:
int customerId = ...
string rval = ...
string proposedName = "John Doe";
UpdateCustomerRecord(id, rval, customer => customer.Name = proposedName);
更新客户的生日:
DateTime proposedBirthday = ...
UpdateCustomerRecord(id, rval, customer => customer.Birthday = proposedBirthday)
这样就不能使用任何不存在的列,也不能将字符串赋给DateTime。
您想在一次通话中更改两个值吗?来吧:
UpdateCustomerRecord(id, rval, customer =>
{
customer.Name = ...;
customer.Birthday = ...;
});
<强>深信?我们来写一下这个函数:
public void UpdateCustomerRecord(int customerId, string rval, Action<Customer> action)
{
// the beginning is as in your function:
var rId = GetRvalId(rval);
// get the customer that you want to update:
using (var _Context = ...)
{
// get the customer you want to update:
var customerToUpdate = _Context.Customers
.Where(customer => customer.Id == Id
&& customer.RVals.Id == rId)
.FirstOrDefault();
// TODO: exception if there is no customerToUpdate
// perform the action and save the changes
action(customerToUpdate);
_context.SaveChanges();
}
简单的纪念活动!
评论后添加
那么这个功能是做什么的?只要你不打电话,它什么都不做。但是,当您调用它时,它会提取客户,对您在调用中提供的客户执行操作,最后调用SaveChanges。
它不会对每个客户执行此操作,不会仅与ID等于提供的ID和customer.RVals.Id == ...的客户执行此操作(您是否仍然确定有多个客户)有这个Id?如果只有一个,为什么要检查RVals.Id?)
因此调用者不仅必须提供定义要更新的客户的Id和RVal,而且他还必须定义必须对此客户执行的操作。
该定义采用以下形式:
customer =>
{
customer.Name = X;
customer.BirthDay = Y;
}
如果您愿意,可以使用除客户之外的其他标识符,但它的含义相同:
x => {x.Name = X; x.BirthDay = Y;}
因为你在调用UpdateCustomerRecord时将它放在 Action 参数的位置,所以我知道x是Customer类型。
Acton声明意味着:如果客户必须更新,我们必须对客户做些什么?你可以把它看作是一个函数:
void Action(Customer customer)
{
customer.Name = ...
customer.BirthDay = ...
}
最后它会做类似的事情:
Customer customerToUpdate = ...
customerToUpdate.Name = X;
customerToUpdate.BirthDay = Y;
SaveChanges();
因此,在第三个参数中,名为 Action ,您可以键入任何您想要的内容,甚至调用与客户无关的函数(可能不明智)。您有一个输入参数,您可以确定它是一个客户。
请参阅我之前调用UpdateCustomerRecord
的示例,最后一个示例:
UpdateCustomerRecord( GetCustomerId(), GetCustomerRVal,
// 3rd parameter: the actions to perform once we got the customerToUpdate:
customer =>
{
DateTime minDate = GetEarliestBirthDay();
if (customer.BirthDay < minDate)
{ // this Customer is old
customer.DoThingsThatOldPeopleDo();
}
else
{ // this Customer is young
customer.DoThingsThatYoungPeopleDo();
}
}
}
因此,Action参数只是一种更简单的说法:“一旦您拥有必须更新的客户,请与客户一起执行此功能
因此,如果您只想更新客户的给定属性,请写下以下内容:
UpdateCustomerRecord(... , customer =>
{
Customer.PropertyThatMustBeUpdated = NewValueOfProperty;
}
当然,这只有在您知道必须更新哪个属性时才有效。但是既然你写了“我正在尝试更新特定的单元格”。我假设您知道此列中的单元格代表哪个属性。
答案 1 :(得分:0)
无法将列名称作为LINQ中的字符串值传递。替代方法,如果你有可以传递的有限数量的列名,那么它可以实现如下:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id &&
x.RVals.Id == rId &&
(x.column1 == value || column == column1) &&
(x.column2 == value || column == column2) &&
(x.column3 == value || column == column3) &&
(x.column4 == value || column == column4) &&
(x.column5 == value || column == column5) &&
)).First();
entry = value;
}
UpdateRecord(5, "rval", "column1", "value");
UpdateRecord(5, "rval", "column2", "value");
UpdateRecord(5, "rval", "column3", "value");
在这里,假设您在调用函数UpdateRecord时可以传递5列,那么您可以在WHERE中添加5个子句,如上所述。
其他方式动态LINQ
var entry = db.Customers.Where(column + " = " + value).Select(...);