Hey dreas, j'ai aussi lutté avec cela et trouvé une solution très élégante.
Vous devez essentiellement utiliser la méthode DataContext.Attach (EntityToUpdate, OriginalEntity).
Il y a quelques pièges ... donc, read this information, it will explain everything.
Une fois que vous l'avez lu, revenez me voir si vous avez des questions. J'ai écrit une classe EntitySaver vraiment utile basée sur cette information, donc si vous en avez besoin, nous pouvons passer par votre classe une fois que vous avez obtenu les gotchas.
acclamations
EDIT: Voici ma classe en plein, au cas où vous voulez l'essayer. Il gère réellement les mises à jour et les insertions automatiquement. laissez-moi savoir que vous avez des questions.
Entité Saver:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QDAL.CoreContext;
using QDAL.CoreEntities;
using LinqExtension.CustomExtensions;
namespace QDAL
{
internal class DisconnectedEntitySaver
{
private QDataDataContext ContextForUpdate;
public DisconnectedEntitySaver() {
ContextForUpdate = Base.CreateDataContext();
}
public List<TEntityType> SaveEntities<TEntityType, TKeyType>(List<TEntityType> EntitiesToSave) {
string PKName;
PKName = Base.GetPrimaryKeyName(typeof(TEntityType), ContextForUpdate);
return SaveEntities<TEntityType, TKeyType>(EntitiesToSave, PKName);
}
public List<TEntityType> SaveEntities<TEntityType, TKeyType>(List<TEntityType> EntitiesToSave, string KeyFieldName)
{
List<TEntityType> EntitiesToPossiblyUpdate;
List<TEntityType> EntitiesToInsert;
List<TEntityType> HandledEntities = new List<TEntityType>();
bool TimeStampEntity;
Type ActualFieldType;
if (EntitiesToSave.Count > 0) {
TimeStampEntity = Base.EntityContainsTimeStamp(typeof(TEntityType), ContextForUpdate);
ActualFieldType = EntitiesToSave.FirstOrDefault().GetPropertyType(KeyFieldName);
if (ActualFieldType != typeof(TKeyType)) {
throw new Exception("The UniqueFieldType[" + typeof(TKeyType).Name + "] specified does not match the actual field Type[" + ActualFieldType.Name + "]");
}
if (ActualFieldType == typeof(string)) {
EntitiesToPossiblyUpdate = EntitiesToSave.Where(ent => string.IsNullOrEmpty(ent.GetPropertyValue<string>(KeyFieldName)) == false).ToList();
EntitiesToInsert = EntitiesToSave.Where(ent => string.IsNullOrEmpty(ent.GetPropertyValue<string>(KeyFieldName)) == true).ToList();
} else {
EntitiesToPossiblyUpdate = EntitiesToSave.Where(ent => EqualityComparer<TKeyType>.Default.Equals(ent.GetPropertyValue<TKeyType>(KeyFieldName), default(TKeyType)) == false).ToList();
EntitiesToInsert = EntitiesToSave.Where(ent => EqualityComparer<TKeyType>.Default.Equals(ent.GetPropertyValue<TKeyType>(KeyFieldName), default(TKeyType)) == true).ToList();
}
if (EntitiesToPossiblyUpdate.Count > 0) {
EntitiesToInsert.AddRange(ResolveUpdatesReturnInserts<TEntityType, TKeyType>(EntitiesToPossiblyUpdate, KeyFieldName));
HandledEntities.AddRange(EntitiesToPossiblyUpdate.Where(ent => EntitiesToInsert.Select(eti => eti.GetPropertyValue<TKeyType>(KeyFieldName)).Contains(ent.GetPropertyValue<TKeyType>(KeyFieldName)) == false));
}
if (EntitiesToInsert.Count > 0) {
ContextForUpdate.GetTable(typeof(TEntityType)).InsertAllOnSubmit(EntitiesToInsert);
HandledEntities.AddRange(EntitiesToInsert);
}
ContextForUpdate.SubmitChanges();
ContextForUpdate = null;
return HandledEntities;
} else {
return EntitiesToSave;
}
}
private List<TEntityType> ResolveUpdatesReturnInserts<TEntityType, TKeyType>(List<TEntityType> PossibleUpdates, string KeyFieldName)
{
QDataDataContext ContextForOrginalEntities;
List<TKeyType> EntityToSavePrimaryKeys;
List<TEntityType> EntitiesToInsert = new List<TEntityType>();
List<TEntityType> OriginalEntities;
TEntityType NewEntityToUpdate;
TEntityType OriginalEntity;
string TableName;
ContextForOrginalEntities = Base.CreateDataContext();
TableName = ContextForOrginalEntities.Mapping.GetTable(typeof(TEntityType)).TableName;
EntityToSavePrimaryKeys = (from ent in PossibleUpdates select ent.GetPropertyValue<TKeyType>(KeyFieldName)).ToList();
OriginalEntities = ContextForOrginalEntities.ExecuteQuery<TEntityType>("SELECT * FROM " + TableName + " WHERE " + KeyFieldName + " IN('" + string.Join("','", EntityToSavePrimaryKeys.Select(varobj => varobj.ToString().Trim()).ToArray()) + "')").ToList();
//kill original entity getter
ContextForOrginalEntities = null;
foreach (TEntityType NewEntity in PossibleUpdates)
{
NewEntityToUpdate = NewEntity;
OriginalEntity = OriginalEntities.Where(ent => EqualityComparer<TKeyType>.Default.Equals(ent.GetPropertyValue<TKeyType>(KeyFieldName),NewEntityToUpdate.GetPropertyValue<TKeyType>(KeyFieldName)) == true).FirstOrDefault();
if (OriginalEntity == null)
{
EntitiesToInsert.Add(NewEntityToUpdate);
}
else
{
ContextForUpdate.GetTable(typeof(TEntityType)).Attach(CloneEntity<TEntityType>(NewEntityToUpdate), OriginalEntity);
}
}
return EntitiesToInsert;
}
protected TEntityType CloneEntity<TEntityType>(TEntityType EntityToClone)
{
var dcs = new System.Runtime.Serialization.DataContractSerializer(typeof(TEntityType));
using (var ms = new System.IO.MemoryStream())
{
dcs.WriteObject(ms, EntityToClone);
ms.Seek(0, System.IO.SeekOrigin.Begin);
return (TEntityType)dcs.ReadObject(ms);
}
}
}
}
Vous aurez besoin de ces aides aussi:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QDAL.CoreContext;
using QDAL.CoreEntities;
using System.Configuration;
namespace QDAL
{
internal class Base
{
public Base() {
}
internal static QDataDataContext CreateDataContext() {
QDataDataContext newContext;
string ConnStr;
ConnStr = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
newContext = new QDataDataContext(ConnStr);
return newContext;
}
internal static string GetTableName(Type EntityType, QDataDataContext CurrentContext) {
return CurrentContext.Mapping.GetTable(EntityType).TableName;
}
internal static string GetPrimaryKeyName(Type EntityType, QDataDataContext CurrentContext) {
return (from m in CurrentContext.Mapping.MappingSource.GetModel(CurrentContext.GetType()).GetMetaType(EntityType).DataMembers where m.IsPrimaryKey == true select m.Name).FirstOrDefault();
}
internal static bool EntityContainsTimeStamp(Type EntityType, QDataDataContext CurrentContext) {
return (CurrentContext.Mapping.MappingSource.GetModel(CurrentContext.GetType()).GetMetaType(EntityType).DataMembers.Where(dm => dm.IsVersion == true).FirstOrDefault() != null);
}
}
}
et ces extensions rendent la réflexion plus facile:
<System.Runtime.CompilerServices.Extension()> _
Public Function GetPropertyValue(Of ValueType)(ByVal Source As Object, ByVal PropertyName As String) As ValueType
Dim pInfo As System.Reflection.PropertyInfo
pInfo = Source.GetType.GetProperty(PropertyName)
If pInfo Is Nothing Then
Throw New Exception("Property " & PropertyName & " does not exists for object of type " & Source.GetType.Name)
Else
Return pInfo.GetValue(Source, Nothing)
End If
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function GetPropertyType(ByVal Source As Object, ByVal PropertyName As String) As Type
Dim pInfo As System.Reflection.PropertyInfo
pInfo = Source.GetType.GetProperty(PropertyName)
If pInfo Is Nothing Then
Throw New Exception("Property " & PropertyName & " does not exists for object of type " & Source.GetType.Name)
Else
Return pInfo.PropertyType
End If
End Function
En fait, je pense que la méthode GetByID est également fausse ;-p Travailler sur un exemple qui corrige les deux (et fonctionne pour POCO ainsi que attribué) –
Hmm mais le GetByID a toujours fonctionné (si je ne me trompe pas). Quelle est votre opinion Marc? –
Eh bien, je ne peux pas voir comment GetPrimaryKey() est implémenté (il n'est pas montré), mais je suppose qu'il regarde les attributs. Ceci est une erreur**. Ce n'est pas ** une exigence de LINQ-to-SQL d'utiliser des attributs; il est également possible d'utiliser un fichier de mappage externe. Dans ce cas, l'attribut (s) n'existera pas, et il échouera. Si vous utilisez le méta-modèle, cela fonctionne pour n'importe quelle implémentation (puisque c'est ainsi que LINQ-to-SQL demande "quelle est la clé primaire"). –