【问题标题】:Help needed for LINQ To SQL operations (insert/update) with nested POCO's使用嵌套 POCO 进行 LINQ To SQL 操作(插入/更新)所需的帮助
【发布时间】:2010-09-22 19:50:34
【问题描述】:

好吧,我一直在尝试将我的模型转换为使用 LINQ,但不想丢弃我当前的 DTO 及其分散在域中的接口。

我设法找到了这篇博客文章,它很好地概述了这个过程:

Achieving POCOs in LINQ To SQL

我已经设法让对象的记录检索正常工作,但是,由于我的模型的嵌套性质,我似乎无法为子对象添加工作。也就是说,如果我创建一个子对象,并设置对所需父对象的引用,LINQ to SQL 仍然会引发异常,说明子对象对父对象的引用为空。如果我尝试添加一个普通的旧父对象,它会成功,但直接添加子对象会失败

这是我失败的测试:

    [Test]
    public void AddSelectionShouldAddSelectionToMarket()
    {
        Market market = (Market) new Repository().GetMarket(1);

        Selection selection = new Selection();
        selection.Market = market;

        new Repository().AddSelection(selection);

        Assert.IsTrue(selection.SID > 0);
    }

这是错误信息:

System.InvalidOperationException:试图删除市场和选择之间的关系。但是,关系的其中一个外键 (Selection.MID) 不能设置为 null。

2个对象的相关部分:

[DataContract]
public class Selection : ISelection
{
    private int mID;
    [DataMember]
    public int MID
    {
        get { return this.mID; }
        set { this.mID = value; }
    }

    private Market market;
    [DataMember]
    public Market Market
    {
        get { return this.market; }
        set
        {
            this.market = value;
            this.mID = value.MID;
        }
    }
}

[DataContract]
public class Market : IMarket
{
    private int mID;
    [DataMember]
    public int MID
    {
        get { return this.mID; }
        protected set { this.mID = value; }
    }

    private List<Selection> selections;
    [DataMember]
    public List<Selection> Selections
    {
        get { return this.selections; }
        set
        {
            this.selections = value;
            // For LINQ
            foreach (Selection selection in selections)
            {
                selection.MID = mID;
                selection.Market = this;
            }
        }
    }
}

我的 DA 代码:

        MarketsDataContext context = new MarketsDataContext();

        DataLoadOptions options = new DataLoadOptions();
        options.LoadWith<Selection>(s => s.Prices);
        options.LoadWith<Market>(m => m.Selections);

        context.LoadOptions = options;
        return context;

和;

    public void AddSelection(ISelection selection)
    {
        using (MarketsDataContext context = MarketsDataContext.GetContext())
        {
            context.Selections.InsertOnSubmit((Selection) selection);
            context.SubmitChanges();
        }
    }

最后是我的 XML 映射:

  <Table Name="dbo.Markets" Member="Markets">
    <Type Name="Market">
      <Column Name="MID" Member="MID" Storage="mID" DbType="Int NOT NULL" IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert" />
      <Association Name="FK_Market-Selections" Member="Selections" Storage="selections" ThisKey="MID" OtherKey="MID" DeleteRule="NO ACTION"  />
    </Type>
  </Table>

  <Table Name="dbo.Selections" Member="Selections">
    <Type Name="Selection">
      <Column Name="SID" Member="SID" Storage="sID" DbType="Int NOT NULL" IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert" />
      <Column Name="MID" Member="MID" Storage="mID" DbType="Int NOT NULL" />
      <Association Name="FK_Market-Selections" Member="Market" Storage="market" ThisKey="MID" OtherKey="MID" IsForeignKey="true" />
    </Type>
  </Table>

那么,谁能指出我正确的方向?我已经找了好几个小时了……

编辑:

这是我的测试失败的堆栈跟踪:

at System.Data.Linq.ChangeTracker.StandardChangeTracker.StandardTrackedObject.SynchDependentData()
at System.Data.Linq.ChangeProcessor.ValidateAll(IEnumerable`1 list)
at System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode)
at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
at System.Data.Linq.DataContext.SubmitChanges()
at BetMax.DataModel.Repository.AddSelection(ISelection selection) in Repository.cs: line 68
at BetMax.DataModel.Test.ModelTest.AddSelectionShouldAddSelectionToMarket() in ModelTest.cs: line 65 

还有我的 GetMarket 方法:

    public IMarket GetMarket(int MID)
    {
        Market market;
        using (MarketsDataContext context = MarketsDataContext.GetContext())
        {
            market = context.Markets.Single(m => m.MID == MID);
        }
        return market;
    }

编辑 2:

好吧,添加

DeleteOnNull="true"

到 XML 映射中的 Selections 外键已删除外键错误,但现在我在 Selections 的一个子对象上获得了一个空引用,说它对 Selections 的引用为空,即使选择正在初始化时没有任何它的变量集(外键之外)。我什至尝试创建一个子对象,并正确设置其引用,但仍然收到此错误:

System.NullReferenceException: Object reference not set to an instance of an object.
at BetMax.DTO.Price.set_Selection(Selection value) in Price.cs: line 25
at System.Data.Linq.Mapping.PropertyAccessor.Accessor`3.SetValue(ref T instance, V value)
at System.Data.Linq.Mapping.MetaAccessor`2.SetBoxedValue(ref Object instance, Object value)
at System.Data.Linq.ChangeProcessor.ClearForeignKeysHelper(MetaAssociation assoc, Object trackedInstance)
at System.Data.Linq.ChangeProcessor.ClearForeignKeyReferences(TrackedObject to)
at System.Data.Linq.ChangeProcessor.PostProcessUpdates(List`1 insertedItems, List`1 deletedItems)
at System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode)
at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
at System.Data.Linq.DataContext.SubmitChanges()
at BetMax.DataModel.Repository.AddSelection(ISelection selection) in Repository.cs: line 68
at BetMax.DataModel.Test.ModelTest.AddSelectionShouldAddSelectionToMarket() in ModelTest.cs: line 69 

Price 是另一个对象,其构造方式与 Selection 与 Market 相关(1 个选择有很多价格,1 个市场有很多选择)等等。

【问题讨论】:

    标签: .net linq-to-sql orm poco


    【解决方案1】:

    我猜问题出在你的测试方法上。您使用 DataContext 创建了一个存储库,但您使用另一个存储库进行了提交。

    [Test]
    public void AddSelectionShouldAddSelectionToMarket()
    {
        Market market = (Market) new Repository().GetMarket(1);
    
        Selection selection = new Selection();
        selection.Market = market;
    
        new Repository().AddSelection(selection);
    
        Assert.IsTrue(selection.SID > 0);
    }
    

    创建一个存储库并在测试方法中使用它。

    [Test]
    public void AddSelectionShouldAddSelectionToMarket()
    {
        Repository repository = new Repository();
        Market market = (Market) repository.GetMarket(1);
    
        Selection selection = new Selection();
        selection.Market = market;
    
        repository.AddSelection(selection);
    
        Assert.IsTrue(selection.SID > 0);
    }
    

    【讨论】:

    • 啊,非常好的观点。我实际上是在每次执行操作时都在处理上下文,所以我会看看是否将其保留更长时间会有所帮助...
    【解决方案2】:

    只是猜测,但它可能就在这里

    public Market Market
    {
        get { return this.market; }
        set
        {
            this.market = value;
            this.mID = value.MID;
        }
    }
    

    当您设置为 Market 的值为 null 时会发生什么?最后一行将无效,因为它无法解析 null.MID。也许你的二传手需要这个:

        set
        {
            this.market = value;
            this.mID = (value == null) ? null : value.MID;
        }
    

    你的 MID 也必须可以为空

    int? MID
    

    【讨论】:

    • 不,这应该不是问题(我认为)。这正是 SidarOK 在博文中采用的方法...
    • 但是正如 GeekyMonkey 提到的,这个控件会让你的类更稳定。因为你的 Market 属性是开放设置为 null,所以检查它是否为 null 不会有问题。
    • 但是等一下,我想确保外键字段(例如选择中的 MID)不为空...允许空值可能会导致数据库中出现孤立条目。
    • 所以你可以抛出异常或在分配 null 时做一些不同的事情。
    【解决方案3】:

    对于您的新问题;对 Price 的选择属性进行空分配时会出现问题。你是通过你的代码做到的吗?您能否再次给出您遇到异常的代码部分?我的意思是分配给 Price 实体...

    根据评论编辑: 我猜这是因为我们之前在 GeekyMonkeys 帖子中提到的空控制异常。在 Selection 类的初始化中,Price 属性需要设置为 null,但是当分配 null 时,它会抛出 null 引用。所以你必须在一组价格属性中做一个空控件。

    private List<Price> prices
    [DataMember]
    public List<Price> Prices
    {
        get { return this.prices; }
        set
        {
            if(value != null)
            {
              this.pricess = value;
              // For LINQ             
              foreach (Price price in prices)
              {
                price.MID = mID;
                price.Selection = this;
              }
           }
        }
    }
    

    【讨论】:

    • 我目前没有触及价格。我现在要做的就是让上面的测试通过,即创建一个新的选择(带有一个空列表)并将其添加到市场对象中。不知何故,我的配置引发了来自 Price 的 null ref 错误?
    • 好的,我会试一试,让你知道我的进展如何。再次感谢!
    【解决方案4】:

    我知道已经有一段时间了,您可能已经解决了这个问题,但也许还没有......

    我假设您的数据结构与此类似:

    Market
    ======
    Market_ID      int not null identity (1, 1)
    
    
    Selection
    =========
    Selection_ID   int not null identity (1, 1)
    Market_ID      int (FK to Market)
    Selection_Name varchar(50)
    

    同时添加一个新市场和一个新选择:

    Selection selection = new Selection();
    Market market = new Market();
    
    market.Selections.Add(selection);
    DataContext.Markets.InsertOnSubmit(market);
    DataContext.SubmitChanges();
    

    向现有市场添加新选择:

    Selection selection = new Selection();
    Market market = DataContext.Markets.Where(a => a.Market_ID == 7).Single();
    
    market.Selections.Add(selection);
    DataContext.SubmitChanges();
    

    要更新市场中的第一个选择:

    Selection selection = DataContext.Markets.Where(a => a.Market_ID == 7).Selections.First();
    
    selection.Selection_Name = "New Name";
    DataContext.SubmitChanges();
    

    【讨论】:

      【解决方案5】:

      我建议将您的代码发送给 Sidar Ok。他是个好人,会为你指明正确的方向。或者至少在他的博客上发表评论,指出他在这里的问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-24
        • 2015-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多