提供基于通用类型集合的通用键比较

Providing a generic key comparison based on a collection of a generic type

我已经为如下几种类型创建了自己的 InsertOrUpdate() 实现:

public IEnumerable<Genre> InsertOrUpdate(IEnumerable<Genre> genres)
{
    foreach (var genre in genres)
    {
        var existingGenre = _context.Genres.SingleOrDefault(x => x.TmdbId == genre.TmdbId);
        if (existingGenre != null)
        {
            existingGenre.Update(genre);
            yield return existingGenre;
        }
        else
        {
            _context.Genres.Add(genre);
            yield return genre;
        }
    }
    _context.SaveChanges();
}

IEnumerable<T> 的 return 类型是必需的,因为它将用于在数据上下文中插入根对象。此方法基本上检索附加对象(如果存在)并使用最新值更新它(如果存在)或将其作为新对象插入(如果不存在)。之后,这个附加对象被 returned,因此它可以链接到多对多表中的根对象。

现在的问题是我有几个这样的集合(流派、海报、关键字等),每种类型的 ID 设置不同:有时称为 TmdbId,有时称为 Id,有时称为Iso。使用一个接口,把它们都重命名为Id是一回事,但问题是它们也是不同的类型:有的是int,有的是string.

问题很简单:我如何将它变成更通用的东西来避免代码重复?

到目前为止我一直在玩弄

public IEnumerable<T> InsertOrUpdate<T>(IEnumerable<T> entities, Func<T, bool> idExpression) where T : class 
{
    foreach (var entity in entities)
    {
        var existingEntity = _context.Set<T>().SingleOrDefault(idExpression);
        if (existingEntity != null)
        {
            _context.Entry(existingEntity).CurrentValues.SetValues(entity);
            yield return existingEntity;
        }
        else
        {
            _context.Set<T>().Add(entity);
            yield return entity;
        }
    }
    _context.SaveChanges();
}

但显然这行不通,因为我无权访问内部 entity 变量。旁注:IDbSet<T>().AddOrUpdate() does not work in my scenario.

你可以试试:

public IEnumerable<T> InsertOrUpdate<T>(IEnumerable<T> entities, Func<T, object[]> idExpression) where T : class

var existingEntity = _context.Set<T>().Find(idExpression(entity));

调用

movie.Genres = new List<Genre>(InsertOrUpdate(movie.Genres, x => new object[] { x.Id }));

(注意returns一个IEnumerable<>的方法非常危险...如果你不列举它,比如

InsertOrUpdate(movie.Genres, x => x.Id);

那么该方法将不会完全执行,因为它会被延迟执行"on demand")

如果只有单键表,可以改成:

public IEnumerable<T> InsertOrUpdate<T>(IEnumerable<T> entities, Func<T, object> idExpression) where T : class

var existingEntity = _context.Set<T>().Find(new object[] { idExpression(entity) });

movie.Genres = new List<Genre>(InsertOrUpdate(movie.Genres, x => x.Id));