2

Forgive me I am not good at EF6. If I make some mistake . Please help me to correct it. Thanks.

Firstly I want to implement the business logic in my service layer like below.

public class userService
{
    void createWithCommit(User user)
    {
        MyEntityContext db= new MyEntityContext ();
        ...
        db.Users.add(user);//Add a new user entity
        ....
        Work w = new Work();
        ... 
        db.Works.add(w);//Add a new work entity
        db.savechanges();//commit the changes which will insert 2 new record . one is user . another is work.
    }
}

But in some service class I want to call multiple others service method in one transaction like below.

class utilService
{
    void update(SomeClass cls)
    {
        using (var tran=new TransactionScope())
        {
          userService userSvr= new userService();
          userSvr.create();//this method already include a savechanges().
          jobService jobSvr= new jobService();
          jobSvr.update();//this method may include another savechanges().
          tran.Complete();//I don't why EF6 doesn't have the Commit method. just has the Complete method.
        }

    }
}

So I can use it in the ASP.net MVC controller like below.

class SomeController
{
    ActionResult index()
    {
        userService userSvr = new userService();
        userSvr.createWithCommit();

    }

    ActionResult util()
    {
        utilService utilSvr = new utilService ();
        userSvr.update(....);

    }
}

So you can see my idea is I want to include multiple service method into one transaction. and each of the included service methods may or may not include the code SaveChanges() (That means a transaction is committed. Right ?).

And you can see . In my test , I tried to use the TransactionScope to include multiple service method into one transaction. I mean it in the method utilService.update(). But It seems the TransactionScope not work after the SaveChanges() is called. So My question is :

Is there any possibility to implement it as my idea ? If there is . What kind of pattern should I apply ? (I heard of UOW and Repository pattern . Are they the solution? Thanks.)

Joe.wang
  • 11,537
  • 25
  • 103
  • 180
  • a unit of work pattern will solve your problem, – Eldho Aug 11 '15 at 13:54
  • This might help you to implement UOW http://stackoverflow.com/questions/19548531/unit-of-work-repository-pattern-the-fall-of-the-business-transaction-concept – Eldho Aug 11 '15 at 13:58
  • Thanks Eldho, Could you please share some sample or read to me . So I can better understand it ? I really don't know how to get start with it . Thanks. – Joe.wang Aug 11 '15 at 14:00
  • http://rob.conery.io/2014/03/04/repositories-and-unitofwork-are-not-a-good-idea/ – Eldho Aug 11 '15 at 14:03

1 Answers1

2

You need to implement Unit of work pattern to avoid transaction scope.Unit of Work and Repository Pattern is something used fairly widely nowadays.

UoW exposes public method Commit to store the changes.

Generic Repository

public class GenericRepository<TEntity> where TEntity : class
{
    internal SchoolContext context;
    internal DbSet<TEntity> dbSet;

    public GenericRepository(SchoolContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

    public virtual IEnumerable<TEntity> Get(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<TEntity> query = dbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (var includeProperty in includeProperties.Split
            (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    }

    public virtual TEntity GetByID(object id)
    {
        return dbSet.Find(id);
    }

    public virtual void Insert(TEntity entity)
    {
        dbSet.Add(entity);
    }

    public virtual void Delete(object id)
    {
        TEntity entityToDelete = dbSet.Find(id);
        Delete(entityToDelete);
    }

    public virtual void Delete(TEntity entityToDelete)
    {
        if (context.Entry(entityToDelete).State == EntityState.Detached)
        {
            dbSet.Attach(entityToDelete);
        }
        dbSet.Remove(entityToDelete);
    }

    public virtual void Update(TEntity entityToUpdate)
    {
        dbSet.Attach(entityToUpdate);
        context.Entry(entityToUpdate).State = EntityState.Modified;
    }
}

UnitOFWork

public class UnitOfWork : IDisposable
{
    //Context Creation in the UnitOfWork
    //This context is supplied to all Repository 
    //Hence One Commit save All Changes
    private SchoolContext context = new SchoolContext();
    private GenericRepository<Department> departmentRepository;
    private GenericRepository<Course> courseRepository;

    public GenericRepository<Department> DepartmentRepository
    {
        get
        {

            if (this.departmentRepository == null)
            {
                this.departmentRepository = new GenericRepository<Department>(context);
            }
            return departmentRepository;
        }
    }

    public GenericRepository<Course> CourseRepository
    {
        get
        {

            if (this.courseRepository == null)
            {
                this.courseRepository = new GenericRepository<Course>(context);
            }
            return courseRepository;
        }
    }

    public void Save()
    {
        context.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Usage

  //Sample Controller
 public class CourseController : Controller
   {
      //Initialize the unit of work here
      private UnitOfWork unitOfWork = new UnitOfWork();

      Public Void MyBussinessTransaction()
      {
         unitOfWork.CourseRepository.Insert(course);
         unitOfWork.DepartmentRepository.Insert(department)
         unitOfWork.Save();
      }
   }

Implementing the Repository and Unit of Work Patterns

Martin Flower explanation of UOW

Community
  • 1
  • 1
Eldho
  • 7,795
  • 5
  • 40
  • 77
  • Thanks, Very nice . But What is the `GenericRepository`? Could you please post some code here .Thanks. – Joe.wang Aug 11 '15 at 15:06
  • @Joe.wang please see the updated answer, you could find more detail of this [here] (http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application) – Eldho Aug 11 '15 at 16:00
  • In your UOW pattern . I found we have to commit transaction in the UI presentation layer. because controller belong to the UI layer.So I wondered If we can do that in the UI layer. I mean call unitOfWork.save() in the controller. – Joe.wang Aug 12 '15 at 02:24
  • @Joe.wang This is based on basic implementation in msdn, Generally this `save()` will be at the data layer and through data transfer objects we transfer data from data to business – Eldho Aug 12 '15 at 04:50
  • yeah , I also found it in the msdn site. +1 – Joe.wang Aug 12 '15 at 05:43