如何从DataContext的跟踪机制中分离LINQ-to-SQL数据对象?

时间:2011-03-09 10:50:47

标签: c# linq linq-to-sql datacontext

在询问this question后,我被告知Table<T>.Attach()方法的工作方式,我还有另外一个问题。

如何从DataContext的状态跟踪机制中分离LINQ-to-SQL数据对象?基本上,我想拉一条记录并更改记录上的数据。但是,当我在同一个SubmitChanges()实例上调用DataContext时,除非我明确调用Attach(),否则我不希望更新记录。这是如何完成的?

5 个答案:

答案 0 :(得分:9)

我强烈建议,如果您要使用LINQ to SQL,,您应该更改设计以适应LINQ to SQL在所有附加修改实体上提交更改的行为。根据我的经验,尝试解决此功能只会导致痛苦。

答案 1 :(得分:7)

从此站点explaining how to detach a linq object将此方法添加到要分离的对象:

public void Detach()
{
    GetType().GetMethod("Initialize", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(this, null);
}

答案 2 :(得分:1)

不幸的是,如果不对实体进行序列化和反序列化,则无法从以前的DataContext中显式分离实体。

您可以为您的目的做的是创建从数据库中提取的对象的副本并使用该副本。在这种情况下,您的原始对象不会受到影响。当需要更新数据库时,您只需将副本附加到DataContext即可。

答案 3 :(得分:0)

我使用Serialize / Deserialize克隆了从DataContext查询的对象,并将新对象提交给同一个DataContext而没有任何问题。如果有任何附加实体,则需要在提交克隆之前使用ID重新获取它们。

/// <summary>Used for serializing and de-serializing objects.</summary>
public static class Serializer
{
    /// <summary>Clones an object.</summary>
    /// <typeparam name="T">The type of object to be cloned.</typeparam>
    /// <param name="source">The object to be cloned.</param>
    /// <returns>A clone of the specified object.</returns>
    public static T Clone<T>(T source)
    {
        return Deserialize<T>(Serialize(source));
    }

    /// <summary>Serializes an object as an XML string.</summary>
    /// <param name="value">A System.Object representing the object to be serialized.</param>
    /// <returns>A System.String representing an XML representation of the specified object.</returns>
    public static string Serialize(object value)
    {
        if (value.GetType() == typeof(string))
        {
            return value.ToString();
        }

        StringWriter stringWriter = new StringWriter();
        using (XmlWriter writer = XmlWriter.Create(stringWriter))
        {
            DataContractSerializer serializer = new DataContractSerializer(value.GetType());
            serializer.WriteObject(writer, value);
        }

        return stringWriter.ToString();
    }

    /// <summary>Creates an object from an XML representation of the object.</summary>
    /// <typeparam name="T">The type of object to be created.</typeparam>
    /// <param name="serializedValue">A System.String representing an XML representation of an object.</param>
    /// <returns>A new object.</returns>
    public static T Deserialize<T>(string serializedValue)
    {
        Type type = typeof(T);
        using (StringReader stringReader = new StringReader(serializedValue))
        {
            using (XmlReader reader = XmlReader.Create(stringReader))
            {
                DataContractSerializer serializer = new DataContractSerializer(type);
                object deserializedValue = serializer.ReadObject(reader);
                return (T)deserializedValue;
            }
        }
    }
}

答案 4 :(得分:0)

在许多情况下,最简单的解决方案是序列化/反序列化对象以轻松克隆它。您使用哪种序列化/克隆方法取决于您。 This question在这方面有很多建议。

我喜欢使用Newtonsoft JSON.NET进行序列化,因为它非常易于使用,具有最低要求(例如,不需要编译器属性),并且我已经将它用于我项目中的其他内容。根据您的使用情况(例如,分离要在UI模型中使用的LINQ / SQL实体),您可能希望擦除数据库ID。一种简单的方法是将自定义DefaultContractResolver类传递给JSON.NET,它将排除ID属性:

    return JsonConvert.SerializeObject(oModel, new JsonSerializerSettings() { ContractResolver = new DNSConfigurationModel.DNSConfigSerializer() });

    /// <summary>
    /// Helper class to ensure that we do not serialize the domainAdvancedDNS child objects 
    /// (we will create our own child collections for serialization). We also suppress serialization of the key ID's.
    /// </summary>
    public class DNSConfigSerializer : DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
            return (from p in properties
                    where p.PropertyName != "DomainAdvancedDNS" &&
                          p.PropertyName != "domainAdvancedDNSSRVs" &&
                          !(p.DeclaringType.Name == "DomainAdvancedDN" && p.PropertyName == "domainAdvancedDNSConfigID") &&
                          p.PropertyName != "DomainAdvancedDNSID" &&
                          p.PropertyName != "domainAdvancedDNSSRVID"
                    select p).ToList();
        }
    }