2011-08-13 56 views
3

我有一個圖像站點,用戶可以標記照片,就像你可以在Stackoverflow上標記問題一樣。如何編寫LINQ to SQL查詢來更新標籤?

我有以下表格:

Images [ID, URL, etc] 
Tags [ID, TagName]  
ImageTag [TagID, ImageID] 

我想寫帶有簽名的方法:

public void UpdateImageTags(int imageId, IEnumerable<string> currentTags) 

此方法將做到以下幾點:

  • 創建任何新當前標籤中的標籤不存在於標籤表中。
  • 獲取圖像的舊ImageTag。
    • 刪除currentTags中不存在的任何ImageTag
    • 添加在當前標籤和oldTags之間新增的任何ImageTag。

這是我在這個方法的嘗試:

public void UpdateImageTags(int imageId, IEnumerable<string> currentTags) 
{ 
    using (var db = new ImagesDataContext()) 
    { 
     var oldTags = db.ImageTags.Where(it => it.ImageId == imageId).Select(it => it.Tag.TagName); 
     var added = currentTags.Except(oldTags); 
     var removed = oldTags.Except(currentTags); 

     // Add any new tags that need created 
     foreach (var tag in added) 
     { 
      if (!db.Tags.Any(t => t.TagName == tag)) 
      { 
       db.Tags.InsertOnSubmit(new Tag { TagName = tag }); 
      } 
     }    
     db.SubmitChanges(); 

     // Delete any ImageTags that need deleted. 
     var deletedImageTags = db.ImageTags.Where(it => removed.Contains(it.Tag.TagName)); 
     db.ImageTags.DeleteAllOnSubmit(deletedImageTags); 

     // Add any ImageTags that need added. 
     var addedImageTags = db.Tags.Where(t => added.Contains(t.TagName)).Select(t => new ImageTag { ImageId = imageId, TagId = t.TagId }); 
     db.ImageTags.InsertAllOnSubmit(addedImageTags); 
     db.SubmitChanges(); 
    } 
} 

然而,這未能就行了:

db.ImageTags.DeleteAllOnSubmit(deletedImageTags); 

與錯誤:

Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.

有沒有e asier我可以處理添加新標籤,刪除舊ImageTags,添加新的ImageTags到LINQ to SQL的操作?

+0

多少標籤TE數據庫存在嗎?這將是一個關鍵因素之間「閱讀所有標籤從數據庫和比較」vs「查詢每個項目」 –

+0

@Marc Gravell,可能會有成千上萬的標籤。也許超過10K,少於50K?總猜測。它仍在發展中,我們已經有近千個了。 – Viktor

+0

在服務器端,TSQL不能做到嗎? –

回答

0

看起來這將是最簡單

public void UpdateImageTags(int imageId, IEnumerable<string> currentTags) 
{ 
    using (var db = new ImagesDataContext()) 
    { 
     var image = db.Images.Where(it => it.ImageId == imageId).First() 
     image.Tags.Clear(); 
     foreach(string s in currentTags) 
     { 
      image.Tags.Add(new Tag() { TagName = s}); 
     } 
     db.SubmitChanges(); 
    } 
} 

這可能對LinqtoSQL稍微修改。 EF是我最近使用的。這也取決於啓用延遲加載。如果不是,你將不得不強制包含圖像標籤。

0

這裏是一個輔助方法來處理許多一對多的關係:

public static void UpdateReferences<FK, FKV>(
    this EntitySet<FK> refs, 
    Expression<Func<FK, FKV>> fkexpr, 
    IEnumerable<FKV> values) 
    where FK : class 
    where FKV : class 
{ 
    Func<FK, FKV> fkvalue = fkexpr.Compile(); 
    var fkmaker = MakeMaker(fkexpr); 
    var fkdelete = MakeDeleter(fkexpr); 

    var fks = refs.Select(fkvalue).ToList(); 
    var added = values.Except(fks); 
    var removed = fks.Except(values); 

    foreach (var add in added) 
    { 
    refs.Add(fkmaker(add)); 
    } 

    foreach (var r in removed) 
    { 
    var res = refs.Single(x => fkvalue(x) == r); 
    refs.Remove(res); 
    fkdelete(res); 
    } 
} 

static Func<FKV, FK> MakeMaker<FKV, FK>(Expression<Func<FK, FKV>> fkexpr) 
{ 
    var me = fkexpr.Body as MemberExpression; 

    var par = Expression.Parameter(typeof(FKV), "fkv"); 
    var maker = Expression.Lambda(
     Expression.MemberInit(Expression.New(typeof(FK)), 
     Expression.Bind(me.Member, par)), par); 

    var cmaker = maker.Compile() as Func<FKV, FK>; 
    return cmaker; 
} 

static Action<FK> MakeDeleter<FK, FKV>(Expression<Func<FK, FKV>> fkexpr) 
{ 
    var me = fkexpr.Body as MemberExpression; 
    var pi = me.Member as PropertyInfo; 

    var assoc = Attribute.GetCustomAttribute(pi, typeof(AssociationAttribute)) 
    as AssociationAttribute; 

    if (assoc == null || !assoc.DeleteOnNull) 
    { 
    throw new ArgumentException("DeleteOnNull must be set to true"); 
    } 

    var par = Expression.Parameter(typeof(FK), "fk"); 
    var maker = Expression.Lambda(
     Expression.Call(par, pi.GetSetMethod(), 
     Expression.Convert(Expression.Constant(null), typeof(FKV))), par); 

    var cmaker = maker.Compile() as Action<FK>; 
    return cmaker; 
} 

用法:

IEnumerable<Tag> values = ...; 
Image e = ...; 
e.ImageTags.UpdateReferences(x => x.Tag, tags);