Как я могу написать запрос LINQ to SQL для обновления тегов? - PullRequest
3 голосов
/ 13 августа 2011

У меня есть сайт с изображениями, где пользователи могут отмечать фотографии так же, как вы можете пометить вопрос в Stackoverflow.

У меня есть следующие таблицы:

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

Я хочу написать методс подписью:

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

Этот метод будет выполнять следующие действия:

  • Создать любые новые теги в currentTags, которые еще не существуют в таблице тегов.
  • Получить старый ImageTag для изображения.
    • Удалить любые теги ImageTag, которые больше не существуют в currentTags
    • Добавить любые теги ImageTag, которые являются новыми между currentTags и oldTags.

Здесьмоя попытка этого метода:

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);

С ошибкой:

Локальная последовательность не может бытьиспользуется в реализациях операторов запросов LINQ to SQL, кроме оператора Contains.

Есть ли более простой способ обработки операции добавления новых тегов, удаления старых тегов ImageTags, добавления новых тегов ImageTags в LINQ to SQL?

Ответы [ 2 ]

0 голосов
/ 22 октября 2011

Вот вспомогательный метод для работы с отношениями «многие ко многим»:

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);
0 голосов
/ 21 октября 2011

Кажется, это будет проще всего

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 - это то, что я использовал совсем недавно. Также это зависит от того, включена ли отложенная загрузка. Если это не так, вам придется принудительно включить теги изображений.

...