2009-01-14 2 views
41

Tous les jours.Utilisez linq pour générer une mise à jour directe sans sélectionner

Je suis encore à apprendre LINQ alors pardonnez-moi si cela est naïf. Lorsque vous traitez directement avec SQL, vous pouvez générer des commandes de mise à jour avec conditions, sans exécuter d'instruction select.

Quand je travaille avec LINQ je semble suivre le modèle de:

  1. entités Sélectionnez
  2. Modifier les entités
  3. Soumettre les modifications

Ce que je veux faire est une mise à jour directe en utilisant linq et l'exécution différée. Est-il possible que l'exécution réelle se produise directement au SQL sans qu'aucune donnée ne soit transmise au client?

DataContext dc = new DataContext 

var q = from product in dc.Products 
     where product.Type = 1 
     set product.Count = 0 

dc.SubmitChanges 

Donc, en substance LINQ a toutes les informations dont il a besoin sans utiliser de sélection pour générer une commande de mise à jour. Il exécuterait le code SQL:

Update Products Set Count = 0 Where Type = 1 

Existe-t-il un mot clé tel que "set" dans LINQ?

+0

la réponse actuellement acceptée est fausse - pouvez-vous choisir la bonne réponse? – laktak

+0

Les réponses ci-dessous ne permettent pas les mises à jour basées sur les ensembles comme je l'ai décrit, mais elles sont intéressantes. – Spence

Répondre

29

Non, ni LINQ, ni LINQ to SQL a des capacités de mise à jour mise en base.

Dans LINQ to SQL, vous devez interroger pour l'objet que vous souhaitez mettre à jour, mettre à jour les champs/propriétés au besoin, puis appelez SubmitChanges(). Par exemple:

var qry = from product in dc.Products where Product.Name=='Foobar' select product; 
var item = qry.Single(); 
item.Count = 0; 
dc.SubmitChanges(); 

Si vous souhaitez faire batching:

var qry = from product in dc.Products where Product.Type==1 select product; 
foreach(var item in qry) 
{ 
    item.Count = 0; 
} 
dc.SubmitChanges(); 

Sinon, vous pouvez écrire la requête vous:

dc.ExecuteCommand("update Product set Count=0 where Type=1", null); 
+1

C'est le code que j'ai, je me demandais si LINQ avait une syntaxe pour cela. Vive la réponse, il est logique d'avoir les mises à jour en C# parce que je suppose que si vous voulez écrire du SQL, vous devez juste écrire sql comme le fait la fonction de commande d'exécution. – Spence

+0

Je suis d'accord; Je préfère l'approche en code. – Randolpho

+9

C'est vraiment stupide, linq to sql devrait avoir un champ de mise à jour – jdelator

3

Linq 2 SQL n'a pas insertion directe/mise à jour/supprimer les équivalents de SQL. Dans V1, les seules mises à jour que vous pouvez faire en utilisant linq sont des SubmmitChanges sur le contexte ou si vous revenez sur sql.

Toutefois, certaines personnes ont tenté de contourner cette limitation de LINQ utilisant des implémentations personnalisées.

Linq batch update.

+0

Cheers mate, c'était une lecture vraiment intéressante. Espérons que cela arrivera en C# 4 ou 5, car la possibilité de mettre à jour et de supprimer par lots est à peu près la seule chose qui manque réellement à LINQ. – Spence

3

Le cadre PLINQO (http://plinqo.com) utilise le LINQ mise à jour batch pour effectuer des mises à jour

context.Task.Update(t => t.Id == 1, t2 => new Task {StatusId = 2});

Ceci effectuera une Update Task Set StatusId = 2 Where Id = 1

43

Vous pouvez effectivement laisser LINQ to SQL générer des instructions de mise à jour:

Foo foo=new Foo { FooId=fooId }; // create obj and set keys 
context.Foos.Attach(foo); 
foo.Name="test"; 
context.SubmitChanges(); 

Dans votre DBML réglé UpdateCheck = "Jamais" pour toutes les propriétés.

Cela génère une seule instruction de mise à jour sans avoir à sélectionner d'abord.

Une mise en garde: si vous voulez être en mesure de définir Nom à null vous devez initialiser votre objet foo à une valeur différente si Linq peut détecter le changement:

Foo foo=new Foo { FooId=fooId, Name="###" }; 
... 
foo.Name=null; 

Si vous voulez vérifier un horodatage alors que la mise à jour, vous pouvez le faire ainsi:

Foo foo=new Foo { FooId=fooId, Modified=... }; 
// Modified needs to be set to UpdateCheck="Always" in the dbml 
+3

Ce n'est pas tout à fait basé cependant. Il est cool de connaître la syntaxe attach si – Spence

+0

Une autre façon de faire est: 'context.Foos.Attach (foo, original: new Foo {FooId = fooId});'. Cela mettra à jour toutes les propriétés qui ne sont pas définies dans l'entité d'origine. – orad

-2

Essayez ceci:

dbEntities.tblSearchItems 
    .Where(t => t.SearchItemId == SearchItemId) 
    .ToList() 
    .ForEach(t => t.isNew = false); 
dbEntities.SaveChanges(); 
+3

Amit, l'appel à "ToList" va charger tous les éléments de la base de données en mémoire. Le foreach fonctionne en mémoire et les changements de saveur les propagent. J'étais après quelque chose qui produirait l'instruction SQL "Update items where condition", qui ne charge aucune donnée localement. – Spence

0

-nous e cette méthode d'extension: EntityExtensionMethods.cs

public static void UpdateOnSubmit<TEntity>(this Table<TEntity> table, TEntity entity, TEntity original = null) 
    where TEntity : class, new() 
{ 
    if (original == null) 
    { 
     // Create original object with only primary keys set 
     original = new TEntity(); 
     var entityType = typeof(TEntity); 
     var dataMembers = table.Context.Mapping.GetMetaType(entityType).DataMembers; 
     foreach (var member in dataMembers.Where(m => m.IsPrimaryKey)) 
     { 
      var propValue = entityType.GetProperty(member.Name).GetValue(entity, null); 
      entityType.InvokeMember(member.Name, BindingFlags.SetProperty, Type.DefaultBinder, 
       original, new[] { propValue }); 
     } 
    } 

    // This will update all columns that are not set in 'original' object. For 
    // this to work, entity has to have UpdateCheck=Never for all properties except 
    // for primary keys. This will update the record without querying it first. 
    table.Attach(entity, original); 
} 

Pour l'utiliser, assurez-vous que l'objet entity que vous passez à la méthode UpdateOnSubmit a toutes les propriétés clés primaires définies pour l'enregistrement que vous souhaitez mettre à jour. Cette méthode met alors à jour l'enregistrement avec les propriétés restantes de l'objet entity sans tirer l'enregistrement en premier.

Après avoir appelé UpdateOnSubmit, veillez à appeler SubmitChanges() pour les modifications à appliquer.

+0

Merci Orad. Ceci est en ligne avec la réponse de laktak, mais a toujours le même problème que c'est vraiment déduire une seule ligne et la connaissance d'une table. Ma question était de savoir si LINQ pouvait finir par envoyer une requête de mise à jour basée sur un ensemble à SQL, ce qui n'est pas le cas. – Spence

+0

Salut @Spence, je ne pense pas qu'une mise à jour par lot 'set' soit possible via LINQ, vous pouvez utiliser la méthode' AttachAll' mais je pense qu'elle exécutera toujours des commandes UPDATE individuelles pour chaque ligne. Ma solution effectue la mise à jour directe sans sélectionner comme dans le titre de la question, mais si votre question spécifique est comment faire une mise à jour par lots, la réponse acceptée est correcte. – orad

Questions connexes