views:

66

answers:

4

I have two generic save methods in a repository class:

public void Save<T>(T entity)
{
    _session.Save(entity);
}

public void Save<T>(IEnumerable<T> entities)
{
    foreach (var item in entities)
    {
        _session.Save(item);
    }
}

However, when I use Save(collection) (which infers the type automatically), it recognizes it as a T rather than IEnumerable<T> and tries to save it using the first method.

How do I write this save method(s) so that it can handle either case, without me having to explicitly provide the type?

A: 

Whjy bother with overloaded methods?, isn't better to do it like:

public void Save<T>(T entity)
{
    if (T is IEnumerable)
    {
         foreach (var item in entity)
              _session..Save(item);
    } else  {
        _session.Save(entity);
    }
}
AlbertEin
This doesn't follow SRP. The fact that the name of the parameter doesn't accurately describe the value it could contain is a big code smell warning.
Rex M
One solution that seems to work elegantly enough is to just split it into two separate methods with different signatures.
Daniel T.
+1  A: 

You can't rely on type inference for this specific case. <T>(T) will satisfy any type, so the inference won't continue searching to find more specific constraints which still meet the signature.

Rex M
A: 

You can use it like this also:

    public void Save<T>(T entity)
    {
        if (typeof(T).GetInterfaces().Contains(typeof(IEnumerable)))
        {
            SaveEnumerable(entity as IEnumerable);
        }
        else
        {
            _session.Save(entity);
        }
    }

    public void SaveEnumerable(IEnumerable entities)
    {
        foreach (var item in entities)
        {
            _session.Save(item);
        }
    }

Hope this helps!!

viky
+3  A: 

Put the type parameter at the class level on your repository instead of at the method level, this way the overload will not be ambiguous.

class Repo<T>
{
    public void Save(T entity)
    {
        _session.Save(entity);
    }

    public void Save(IEnumerable<T> entities)
    {
        foreach (var item in entities)
        {
            _session.Save(item);
        }
    }
}

You'd use it like this:

var repo = new Repo<Person>();

var p = new Person();
repo.Save(p);

var ps = new Person[] { p };
repo.Save(ps);
joshperry
What do I do if I want to keep the repository typeless? I'd like to be able to instantiate the repository and query for any entity by passing a type to `Get()`. This saves me the trouble of instantiating multiple copies of the same repository where the only difference between them is the type.
Daniel T.
To clarify, I'd like to be able to do this: `var repo = new Repository(); repo.Get<Ark>(); repo.Get<Animal>(); repo.Get<Food>();` instead of: `var repo1 = new Repository<Ark>(); var repo2 = new Repository<Animal>(); var repo3 = new Repository<Food>();`.
Daniel T.