Linq使用lambda表达式进行更新时出现问题

3

我正在尝试使用Lambda编写一些Linq代码。这是我第一次使用Lambda,我在更新记录时遇到了问题。我的代码如下:

using (DataClasses1DataContext db = new DataClasses1DataContext())
{

    Table<NOTIF_RECIP> NOTIF_RECIP_alias = db.GetTable<NOTIF_RECIP>();
    Table<NOTIF_SCHED> NOTIF_SCHED_alias = db.GetTable<NOTIF_SCHED>();
    Table<mainframe_replication> mainframe_replication_alias = db.GetTable<mainframe_replication>();

    var ids = NOTIF_SCHED_alias.Select(x => x.NOTIF_RPT_ID).ToArray();


    foreach (string notif_sched_data in ids)
    {
        var repljoinmf = mainframe_replication_alias
                        .Join(NOTIF_RECIP_alias,
                             mfr => mfr.RPT_ID,
                             nr => nr.NOTIF_RECIP_ID,
                             (mfr, nr) => new
                             {
                                 ReportId=mfr.RPT_ID, 
                                 Reportversion=mfr.RPT_VERS,
                                 ReportBytes= mfr.RPT_BYTES.ToString(), 
                                 ReportDate=mfr.REPL_DTM.ToString(),
                                 NotifId= mfr.NOTIF_ID,
                                 RecipAdd=nr.NOTIF_RECIP_ADDR
                             });

        foreach(var repljoinmf_data in repljoinmf)
        {
            //DO STUFF 
            repljoinmf_data.NotifId = "Changedxyz";
            //db.SubmitChanges();
        }
    }
}

我在执行repljoinmf_data.NotifId = "Changedxyz";时遇到了错误。

错误信息为:Error 2 Property or indexer 'AnonymousType#3.NotifId' cannot be assigned to -- it is read only

请问有人可以帮我解决一下这个问题吗?我想是因为使用了匿名变量 var 所导致的。请大家给予帮助,感激不尽。

谢谢!


4
var不是匿名的,它的意思是“让编译器决定类型”。 - D Stanley
@DStanley 感谢您的澄清。 - v2v2
3个回答

1
作为错误提示,一旦匿名类实例化后就无法修改。
虽然可以切换到强类型类,然后重新分配成员属性,但是你有机会将前面的LINQ语句中所需的结果投影到相同的匿名类中。
var repljoinmf = mainframe_replication_alias
.Join(NOTIF_RECIP_alias, mfr => mfr.RPT_ID, nr => nr.NOTIF_RECIP_ID, 
(mfr, nr) => new // Anon Class projection
{ 
    ReportId=mfr.RPT_ID, 
    Reportversion=mfr.RPT_VERS,
    ReportBytes= mfr.RPT_BYTES.ToString(),     
    ReportDate=mfr.REPL_DTM.ToString(),
    NotifId= "Changedxyz", // *** No need to mutate this afterwards
    RecipAdd=nr.NOTIF_RECIP_ADDR 
});

Edit,Update 不是简单的赋值操作,建议采用替代方案。
选项#1:强类型类,在投影后进行变化。
添加一个新的类(我猜测了一些类型)。
public class MyPoco
{
     public int ReportId {get; set;}
     public string Reportversion {get; set;}
     public byte[] ReportBytes {get; set;}
     public DateTime ReportDate {get; set;}
     public int NotifId {get; set;}
     public string RecipAdd {get; set;}
}

你可以将其投射到指定类名而非匿名类中:

(mfr, nr) => new MyPoco // Not anonymous
{ 
    ReportId=mfr.RPT_ID, 
    ...

然后进行修改:

foreach(var repljoinmf_data in repljoinmf)
{
  repljoinmf_data.NotifId = "SomeNewValue"

选项 #2 - 创建一个方法(或Func),执行复杂逻辑

由于您似乎已经将所有数据实例化,因此可以在属性投影中使用复杂函数。任何可用的本地变量(闭包)都可以传递给该函数,join lambda参数(mfr,nr)也是如此。

例如,编写一个函数来计算您的NotifId =“Changedxyz”替换:

private string DoIntensiveLogic(mainframe_replication mfr, NOTIF_RECIP nr)
{
    // Do Stuff
}

接着您可以在原始匿名投影中使用它:

(mfr, nr) => new // Anon Class projection
{ 
    ReportId=mfr.RPT_ID, 
    Reportversion=mfr.RPT_VERS,
    ReportBytes= mfr.RPT_BYTES.ToString(),     
    ReportDate=mfr.REPL_DTM.ToString(),
    NotifId= DoIntensiveLogic(mfr, nr), // Call the function each row
    RecipAdd=nr.NOTIF_RECIP_ADDR 
});

谢谢,但我需要根据一些条件每次更改Notifid的值。我不希望它固定为“Changedxyz”。 - v2v2
我已经更新了两种实现方法。我更喜欢第二种选项,因为它避免了新类,并且可以一步完成投影。在我的看法中,事后强制更改结果总是有点不好看。 - StuartLC
谢谢您,先生。您是救命恩人。现在一切都正常了,但是当我调用 db.SubmitChanges() 时,数据库中没有任何更改。我还漏掉了什么吗?再次感谢。 - v2v2
请您看一下为什么它无法提交到数据库中,我现在卡在这里了。我知道这是一个不同的问题,但我不想发布一个新的问题。再次感谢您,先生。 - v2v2

1

匿名类型是不可变的,因此创建后无法更改,您必须创建一个新类型。
要解决您的问题,您需要创建自己的类型,并在需要未来更新时避免使用匿名类型。
您的类型可能如下所示:

public class  ReportInfo
{
   public  int Id{get; set;} 
//the same thing  for others properties 
}

你的查询将会是这样的。
new ReportInfo() { 
                Id = mfr.RPT_ID, 
                Reportversion = mfr.RPT_VERS,
                ReportBytes = mfr.RPT_BYTES.ToString(), 
                ReportDate = mfr.REPL_DTM.ToString(),
                NotifId = mfr.NOTIF_ID, 
                RecipAdd = nr.NOTIF_RECIP_ADDR 
            })

你可以很容易地更新你的属性

 foreach(var repljoinmf_data in repljoinmf)
        {
            //DO STUFF 
             repljoinmf_data.NotifId = "Changedxyz";
           //db.SubmitChanges();
        }

关于匿名类型的更多信息
编译器实际上在做什么。当您编写如下代码行时:

var o = new { property1 = expression1, ..., propertyN = expressionN };

编译器推断每个表达式的类型,创建这些推断类型的私有字段,为每个字段创建公共只读属性,并创建一个接受所有这些表达式的构造函数。构造函数的代码从传递给它的表达式结果初始化私有只读字段。此外,编译器还覆盖了Object的Equals、GetHashCode和ToString方法,并在所有这些方法内生成代码。

非常感谢您,但是我的错,只能标记一个答案。再次感谢您。 - v2v2

0
如果您想在稍后更改“NotifId”,则可以按ID查找记录并更改属性。
示例:
var alias = mainframe_replication_alias.SingleOrDefault(mfr => mfr.NOTIF_ID == repljoinmf_data.NotifId);
if(alias != null)
   alias.NOTIF_ID = "Changedxyz";

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接