This is a migrated thread and some comments may be shown as answers.

How to design OpenAccess ORM in my framewok?

3 Answers 79 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
john
Top achievements
Rank 1
john asked on 25 Sep 2013, 07:34 AM
I have a framework to include those common functions such as security, logging and so on. Before, I used Enterprise Library DataAccess block as my data access layer. Now I want to switch it to use OpenAccess ORM. Since the security, logging all have owner table. So my framework will provide some helper class to expose API for developer calling. I will create a model class to include those tables. Assumption, I have one project which will use the framework. So the following is my problem:

1. Whether my framework need to include context or only model class?
2. For example, in project,  one button clicking event will call project API to insert data to table A and call framework API to insert data to security table. How can I ensure the two methods is involved in the same transaction? 

3 Answers, 1 is accepted

Sort by
0
Kaloyan Nikolov
Telerik team
answered on 27 Sep 2013, 10:20 AM
Hello John,

There several ways to achieve that. I will try to show you one of the most advanced and the most simpler way. I hope you will manage to implement the proper combination considering the specifics of your project. 

1. Using the Repository Pattern:
You could wrap all database operations in a dedicated layer in your architecture. Often it is called repository. Here is a simplified implementation of such repository base class, which encapsulates all of your security / logging code:

public partial abstract class Repository<TEntity> : IRepository<TEntity>
        where TEntity : class
{
    protected IEntitiesModel1UnitOfWork dbContext;
 
    public Repository(IEntitiesModel1UnitOfWork unitOfWork)
    {
        this.dbContext = unitOfWork;
    }
 
    public void Add(TEntity entity, UserInfo userInfo)
    {
        this.dbContext.Add(entity);
        this.dbContext.SaveChanges();
    }
 
    public void Remove(TEntity entity, UserInfo userInfo)
    {
        this.dbContext.Delete(entity);
 
        var log = new SecurityLog()
        {
            ActionType = ActionType.Delete,
            EntityType = entity.GetType().Name,
            User = userInfo.Login,
            Date = DateTime.Now
        };
 
        this.dbContext.Add(dbContext);
        this.dbContext.SaveChanges();
    }
 
    public IQueryable<TEntity> GetAll()
    {
        return this.dbContext.GetAll<TEntity>();
    }
 
    public TEntity Get(ObjectKey objectKey)
    {
        return this.dbContext.GetObjectByKey<TEntity>(objectKey);
    }
 
    public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
    {
        return this.dbContext.GetAll<TEntity>().Where(predicate);
    }
}

You can have a look in the Remove method. As both, the deletion of the target entity and the audit trail insert will happen when the dbContext.SaveChanges() is invoked they will use single transaction. Then you can be sure that the data will be consistent. 

 Such implementation uses generic methods in abstract class, then you should implement one repository class for each entity type you would like to expose to your Business Layer. The implementation would be pretty simple: 
public partial interface IRegionRepository : IRepository<Region>
{
}
 
public partial class RegionRepository : Repository<Region>, IRegionRepository
{
    public RegionRepository(IEntitiesModel1UnitOfWork unitOfWork)
        : base(unitOfWork)
    {
    }
}

the basic CRUD functionality is implemented by the basic code but you still are able to override some of the methods or add more specific methods if needed. 

 This approach is used by OpenAccess ORM to generate Plain WCF Data Services. You can create a services and see the repository implementation we consider helpful. 
Also you could employ T4 Code generation to automate the generation of the repository classes.

2. The most easy way to record all changes in you database would be to use our  Context Tracking Events. You can use them to cancel operations for those users that don't have access for a given resource or to add a record in an audit trail table after a given change. This approach doesn't ensure that all operations will happen in one and the same transaction. 

I hope this helps. Please do not hesitate to get back to us with any questions.

Regards,
Kaloyan Nikolov
Telerik
OpenAccess ORM Q3 2013 Beta is available for immediate download in your account. Get it now and play with the latest bits. See what's new >>
0
john
Top achievements
Rank 1
answered on 27 Sep 2013, 03:23 PM
Hi,

Thanks for your good suggestion. Maybe my situation is not same as your description.
For example:
In my framework(a component), I have static SecurityHelper class which have a static method named InsertUserAccount. The method will accept UserAccount entity, then insert data to UserAccount table.
In my application, I referenced the framework dll. The save button of User setup page will save user account information and user profile information. So the button event will call SecurityHelper.InsertUserAccount first, then call another business method to insert user profile information.

Before, I used Enterprise Library DataAccess layer block. I will use TransactionScope to control the transaction as below:
Using(TransactionScope scope = new TransactionScope())
{
       SecurityHelper.InsertUserAccount() //frameowork method
       InsertUserProfile()
}

Now, if I switch to use OpenAccess ORM, how can I write the code of SecurityHelper.InsertUserAccount? Because I need to ensure two methods in the same transaction. So whether I need to call Context.SaveChange in SecurityHelper.InsertUserAccount?  As I know, in OpenAccess ORM, the SaveChanges method will commit the transaction. But if I call SaveChanges in InsertUserAccount, it will not keep two method in the same transaction. If I don't call SaveChanges  in InsertUserAccount, how the method can be used alone














0
Kaloyan Nikolov
Telerik team
answered on 01 Oct 2013, 08:35 AM
Hello John,

I would say that the easiest way to achieve this is with code like this:

using(var dbContext = new MyOAContext())
{
       SecurityHelper.InsertUserAccount(dbContext );  //call dbContext.FlushChnages() inside
       InsertUserProfile(dbContext );                 //call dbContext.FlushChnages() inside
        
       dbContext.SaveChnages();     //commits the transaction
}

You should ensure a single instance of you context is passed as a parameter to your business methods and inside them you should use dbContext.FlushChanges(). It will execute all queries against the database but will keep the transaction open. The final dbContext.SaveChanges() will commit it. If an exception is raised the transaction will be rolled back when the code leaves the using block. 

You should try to avoid using the TransactionScope when possible because it escalates the transactions to the Distributed Transaction Coordinator which works slower compared to the regular database transaction. TransactionScope should be used when you want to handle operations with different databases or even with different servers in a single transaction. The above demo code will use simple database transaction. 

I hope this helps. 


Regards,
Kaloyan Nikolov
Telerik
OpenAccess ORM Q3 2013 Beta is available for immediate download in your account. Get it now and play with the latest bits. See what's new >>
Tags
General Discussions
Asked by
john
Top achievements
Rank 1
Answers by
Kaloyan Nikolov
Telerik team
john
Top achievements
Rank 1
Share this question
or