在LINQtoSQL中,如何通过编辑父模型来更新子关系?

时间:2010-10-05 15:47:50

标签: c# asp.net asp.net-mvc linq-to-sql

这是我第一次尝试使用LINQtoSQL更新数据库。至少,如果你不计算我所遵循的教程,这是我的第一个。不幸的是,我发现的教程提供的不仅仅是更新单个表。我正在尝试更新一个更复杂的数据库模型。

我有一个Stream表:

[Table]
public class Stream
{
    [HiddenInput(DisplayValue = false)]
    [Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
    public long StreamID { get; set; }

    /** Other columns removed for brevity **/

    // relationship:
    private EntitySet<Stream2FieldTypes> _Stream2FieldTypes;
    [System.Data.Linq.Mapping.Association(Storage = "_Stream2FieldTypes", OtherKey = "StreamID")]
    public EntitySet<Stream2FieldTypes> Stream2FieldTypes
    {
        get { return this._Stream2FieldTypes; }
        set { this._Stream2FieldTypes.Assign(value); }
    }

我有一张Stream2FieldTypes表:

[Table]
public class Stream2FieldTypes
{
    [Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
    public long s2fID { get; set; }
    public long StreamID { get; set; }     // FK

    /** other columns removed for brevity **/

    // relationship (one Stream2FieldTypes to many Streams) 
    private EntitySet<Stream> _Stream;
    [Association(Storage = "_Stream", ThisKey = "StreamID")]
    public EntitySet<Stream> Stream
    {
        get { return this._Stream; }
        set { this._Stream.Assign(value); }
    }

现在,我正在尝试更新模型,以便我可以将更新发送到存储库以保留到DataContext。我无法更新Stream.Stream2FieldTypes,因为get设置为私有EntitySet

当我无法更改Stream.Stream2FieldTypes时,如何更新Stream.Stream2FieldTypes,因为它是私有的EntitySet&lt;&gt;?

编辑:伪代码

基本上,我认为我应该可以使用编辑操作中的命令更新StreamStream2FieldTypes表,如下所示:

myRepository.SaveStream(stream);

我一直在努力做这样的事情:

        if (ModelState.IsValid)
        {
            // Convert StreamEditModel to Stream
            var stream = new Genesis.Domain.Entities.Stream
            {
                StreamID = form.StreamID,
                StreamUrl = form.StreamUrl,
                StreamName = form.StreamName,
                StreamBody = form.StreamBody,
                StreamTitle = form.StreamTitle,
                StreamKeywords = form.StreamKeywords,
                StreamDescription = form.StreamDescription,
                Stream2FieldTypes = new EntitySet<Stream2FieldTypes>()
            };

            // Loop to convert Stream2FieldTypes to Steam2FieldTypesEditModel
            foreach (var item in form.Stream2FieldTypes)
            {
                var fieldTypeEntry = new Stream2FieldTypes
                {
                    FieldTypeID = item.FieldTypeID,
                    s2fID = item.s2fID,
                    s2fIsRequired = item.s2fIsRequired,
                    s2fLabel = item.s2fLabel,
                    StreamID = item.StreamID,
                };
                stream.Stream2FieldTypes.Add(fieldTypeEntry); // Add to list
            }

            genesisRepository.SaveStream(stream);

            return RedirectToAction("Index");
        }
        else 
        {
            return View(form);
        }

当我尝试运行此代码时,出现此错误:

  

异常详细信息:System.NullReferenceException:未将对象引用设置为对象的实例。

     

来源错误:

     

第58行:{

     

第59行:get {return this._Stream2FieldTypes; }

     

行错误:第60行:设置{this._Stream2FieldTypes.Assign(value); }

     

第61行:}

     

第62行:

1 个答案:

答案 0 :(得分:3)

SaveStream方法中,您可以像StreamID一样设置StreamID = item.StreamID属性。因为您有Stream ID,我希望您尝试更改现有的Stream类。但是,在查看代码时,您将创建一个新代码。我认为您遇到的问题是由此造成的。

您应该做的是以下内容:

  • 区分创建新实体和改变现有实体。
  • 尝试尽可能减少ID属性的使用。与实体本身合作。这使您的代码更清晰,更易读。 (我总是将ID属性设置为内部,EntitySetEntityRef公开。

例如,我认为您的SaveStream应该更像下一个代码。仔细观察,这段代码缺少如下行:

  • FieldTypeID = item.FieldTypeID
  • StreamID = form.StreamID

这是因为这根本不会起作用。您需要首先从数据库中检索现有实体并进行更新。您无法创建新对象,将其ID设置为数据库中的现有记录,并期望LINQ to SQL为您更新该记录。这不是LINQ to SQL的设计方式。

以下是可能适合您的示例:

public void SaveStream(StreamEditModel stream)
{
    if (!ModelState.IsValid)
    {
        return;
    }

    if (stream.Id == 0)
    {
        CreateStream(stream);
    }
    else
    {
        UpdateStream(stream);
    }
}

private void CreateStream(StreamEditModel form)
{
    var stream = new Stream();

    FillStream(stream, form);

    UpdateStream2FieldTypes(stream, form);

    genesisRepository.SubmitChanges();
}

private void UpdateStream(StreamEditModel form)
{
    var stream = genesisRepository.GetById(stream.StreamID);

    FillStream(stream, form);

    UpdateStream2FieldTypes(stream, form);

    genesisRepository.SubmitChanges();
}

private void FillStream(Stream stream, StreamEditModel form)
{
    stream.StreamUrl = form.StreamUrl;
    stream.StreamName = form.StreamName;
    stream.StreamBody = form.StreamBody;
    stream.StreamTitle = form.StreamTitle;
    stream.StreamKeywords = form.StreamKeywords;
    stream.StreamDescription = form.StreamDescription;
}

private void UpdateStream2FieldTypes(Stream stream,
    StreamEditModel form)
{
    var typesToDelete =
        from type in stream.Stream2FieldTypes
        let ids = form.Stream2FieldTypes.Select(t => t.FieldTypeID)
        where !ids.Contains(type.FieldTypeID)
        select type;

    genesisRepository.RemoveStream2FieldTypes(typesToDelete);

    var typesToAdd =
        from type in form.Stream2FieldTypes
        where type.FieldTypeID == 0
        select CreateStream2FieldTypes(type);

    foreach (var typeToAdd in typesToAdd)
    {
        stream.Stream2FieldTypes.Add(typeToAdd);
    }

    var formTypesToUpdate = 
        from type in form.Stream2FieldTypes
        where type.FieldTypeID != 0
        select type;

    foreach (var modelToUpdate in formTypesToUpdate)
    {
        var typeToUpdate = stream.Stream2FieldTypes.Single(
            t => t.FieldTypeID == modelToUpdate.FieldTypeID);

        FillStream2FieldTypes(typeToUpdate, typeToUpdate);
    }
}

private static  Stream2FieldTypes CreateStream2FieldTypes(
    Stream2FieldTypesEditModel form)
{
    var fieldType = new Stream2FieldTypes();

    FillStream2FieldTypes(fieldType, form);

    return fieldType;
}

private static void FillStream2FieldTypes(
    Stream2FieldTypes type, 
    Stream2FieldTypesEditModel item)
{
    type.s2fID = item.s2fID;
    type.s2fIsRequired = item.s2fIsRequired;
    type.s2fLabel = item.s2fLabel;
}

干杯