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

孤街浪徒 提交于 2019-12-11 04:28:45

问题


I have created my own InsertOrUpdate() implementations for a few types like this:

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

The return type of IEnumerable<T> is required because it will be used to insert the root object in the datacontext. This method basically retrieves the attached object if it exists and updates it with the newest values if it does or inserts it as a new object if it doesn't. Afterwards this attached object is returned so it can be linked to the root object in the many-to-many tables.

The problem now is that I have several of these collections (genres, posters, keywords, etc) and each type's ID is differently setup: sometimes it's called TmdbId, sometimes Id and sometimes Iso. It's one thing to use an interface and rename them all to Id but the problem exists in that they are also different types: some are int and some are string.

The question is easy: how I do turn this into something more generic to avoid this code duplication?

So far I have been toying around with

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

but obviously this won't work since I have no access to the inner entity variable. Sidenote: IDbSet<T>().AddOrUpdate() does not work in my scenario.


回答1:


You could try:

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

and

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

called with something like

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

(note that a method that returns a IEnumerable<> is very dangerous... If you don't enumerate it, like

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

then the method won't be executed fully, because it will be lazily executed "on demand")

If you only have single-key tables, you can change it to:

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

and

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

and

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


来源:https://stackoverflow.com/questions/29063445/providing-a-generic-key-comparison-based-on-a-collection-of-a-generic-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!