views:

94

answers:

3

I have three database operations like so:

public void Add<T>(T entity)
{
    using (var transaction = Session.BeginTransaction())
    {
        if (entity is IEnumerable)
        {
            foreach (var item in (IEnumerable) entity)
            {
                Session.Save(item);
            }
        }
        else
        {
            Session.Save(entity);
        }

        transaction.Commit();
    }
}

public void Update<T>(T entity)
{
    using (var transaction = Session.BeginTransaction())
    {
        if (entity is IEnumerable)
        {
            foreach (var item in (IEnumerable) entity)
            {
                Session.Update(item);
            }
        }
        else
        {
            Session.Update(entity);
        }

        transaction.Commit();
    }
}

public void Delete<T>(T entity)
{
    using (var transaction = Session.BeginTransaction())
    {
        if (entity is IEnumerable)
        {
            foreach (var item in (IEnumerable)entity)
            {
                Session.Delete(item);
            }
        }
        else
        {
            Session.Delete(entity);
        }

        transaction.Commit();
    }
}

As you can see, the only thing that differs is the Session.[something] part. How would I refactor this into only one method?

A: 

You can make one method, and have it accept a delegate as a parameter, that specifies the action you want to do.

private void DatabaseAction<T>(T entity, Action<T> action) 
{
    using (var transaction = Session.BeginTransaction())
    {
        if (entity is IEnumerable)
        {
            foreach (var item in (IEnumerable) entity)
            {
                action(item);
            }
        }
        else
        {
            action(item);
        }

        transaction.Commit();
    }
}

Then refactor your 3 methods to:

public void Add<T>(T entity)
{
    DatabaseAction(entity, item => Session.Save(item));
}

public void Update<T>(T entity)
{
    DatabaseAction(entity, item => Session.Update(item));
}

public void Delete<T>(T entity)
{
    DatabaseAction(entity, item => Session.Delete(item));
}
driis
This doesn't compile (cannot convert item from object to T).
dtb
That is because your foreach loop works on objects. If the Session.Xxx methods is meant to work on any object, change Action<T> to Action<object>. If the entity being passed in should contain T instances, then change your code so you enumerate on the generic version of IEnumerable.
driis
A: 

You could pass in an Action<T> that should be performed for the entity:

public static void DoInTransaction<T>(this T entity, Action<T> action)
{
    using (var transaction = Session.BeginTransaction())
    {
        if (entity is IEnumerable<T>)
        {
            foreach (T item in (IEnumerable<T>) entity)
            {
                action(item);
            }
        }
        else
        {
            action(entity);
        }

        transaction.Commit();
    }
}

Example:

entity.DoInTransaction(Session.Save);
entity.DoInTransaction(Session.Update);
entity.DoInTransaction(Session.Delete);
dtb
you are breaking the correct interface...
Hellfrost
Hellfrost, can you elaborate?
Daniel T.
A: 

Delegates look like a good solution here, as explained by other answerers. One other possible refactoring that could arguably simplify the inside logic would be a function that returns an IEnumerable. If the passed-in object is an IEnumerable, just return it; otherwise build a single-element enumeration with the passed-in object and return the enumeration. This would turn this:

if (entity is IEnumerable)
{
    foreach (var item in (IEnumerable) entity)
    {
       Session.Save(item);
    }
}
else
{
    Session.Save(entity);
}

into this:

foreach (var item in ForceEnumerable(entity))
    {
       Session.Save(item);
    }
Carl Manaster
Hmm, that's an interesting approach!
Daniel T.