Showing posts with label ORM. Show all posts
Showing posts with label ORM. Show all posts

Thursday, December 9, 2010

Using the EntityFramework POCO repository implementation in WCF – Part 2

The POCO entities that we used in the WCF implementation in the previous post are detached from the object context when passed to the client application. Before trying to perform operations like Save, insert or delete we need to attach these objects to the context and mention the change on the entity so that the Context.SaveChanges method updates the model accordingly. Let’s see an example on how the Customer object with some orders added and which have some changes are updated in the DB when the update method is called on the repository.
public override void Update(Customer entity)
{
    OrderRepository orderRepository = new OrderRepository(this.Context as IUnitOfWork);
    orderRepository.SaveAll(entity.Orders);
    base.Update(entity);
}

The CustomerRepository’s Update method first updates/ adds orders via the order repository. The order repository has a SaveAll method that iterates through all orders and perform the necessary operations on the order.
public class OrderRepository : BaseRepository<Order>, IOrderRepository
{  

    public void SaveAll(IEnumerable<Order> orders)
    {
        orders.ToList().ForEach(order =>
            {
                if (order.Id == Guid.Empty)
                    Add(order);
                else
                    Update(order);
            });           
    }
}

The BaseRepository also has some changes inorder to attach and add the additional information on the attached entity to do the CRUD.
public abstract class BaseRepository : EF4Recipies.Repository.IRepository where T : BaseEntity
{
   
    public virtual void Add(T entity)
    {
        entity.Id = entity.Id == Guid.Empty ? Guid.NewGuid() : entity.Id;
        entity.CreatedDate = entity.ChangedDate = DateTime.Now;
        ValidateAndAddToObjectSet(entity);
    }

    private void ValidateAndAddToObjectSet(T entity)
    {
        entity.Validate();
        Attach(entity);
        Context.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Added);
    }

    public virtual void Remove(T entity)
    {
        Attach(entity);
        Context.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Deleted);
        Context.DeleteObject(entity);
    }

    public virtual void Update(T entity)
    {
        Attach(entity);
        Context.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Modified);
        var entityFromDb = GetById(entity.Id);
        if (entityFromDb != null && StructuralComparisons.StructuralEqualityComparer.Equals(entity.Timestamp, entityFromDb.Timestamp))
        {
            entity.ChangedDate = DateTime.Now;
            entity.Validate();
        }
        else
            throw new InvalidOperationException("Concurrency exception");
    }
}
Test cases:
[TestMethod]
public void UpdateOrders_should_insert_new_orders_created_and_save_the_changes_on_the_existing_ones()
{
    var proxy = new ShoppingCartServiceClient.ShoppingCartServiceClient("WSHttpBinding_IShoppingCartService");
    var customerWithOrders = proxy.GetById(new Guid("1931FEDC-2CCA-437E-A3BD-D5842879530D"));
    if (customerWithOrders != null)
    {
        customerWithOrders.Email = "myNewEmail@mailserver.com";
        customerWithOrders.Orders.Add(
            new Order
            {
                OrderDate = DateTime.Now,
                OrderNumber = string.Format("ORD{0:MMddYYYYHHmmss}", DateTime.Now),
                CustomerId = customerWithOrders.Id,
                Customer = customerWithOrders
            });

        var existingOrder = customerWithOrders.Orders.First(o => o.Id != Guid.Empty);
        existingOrder.OrderNumber = string.Format("ORDCH{0:MMddYYYYHHmmss}", DateTime.Now);
        proxy.UpdateOrders(customerWithOrders);
        var order = proxy.SearchByOrderNumberAndCustomerDetails("ORDCH", customerWithOrders);
        Assert.IsNotNull(order);

    }
    else
        Assert.IsFalse(true, "Failed to load customer with orders");
}

Tuesday, December 7, 2010

Using the EntityFramework POCO repository implementation in WCF – Part 1

Entity Framework 4.0 allows creation and usage of POCO entities for implementing persistence ignorant entity types from an Entity Data Model. We can use a WCF service and expose operation contracts to the clients so that they can perform operations like insert, save, get etc on these entities. In this post I’ll use the sample created in the post implementation of the Repository using EF 4.0 POCO objects and expose them via a WCF service.
We need to make some changes to the object model and context to expose it using WCF. The object model which I have created has multiple references within itself when being serialized. For e.g. the customer can have multiple orders and the order has a reference back to the customer. When the WCF's data contract serializer attempts to serialize this object, it results in a stack overflow exception due to the cyclic relationship between the parent-child relation. .NET 3.5 SP1 includes a IsReference parameter in the DataContract attribute which will automatically retain the cyclic object relationships for the objects. We can use this attribute on the base entity in our sample to mention cyclic references.
[DataContract(IsReference = true)]
public abstract class BaseEntity
{
    [DataMember]
    public Guid Id { get; set; }

    [DataMember]
    public DateTime CreatedDate { get; set; }

    [DataMember]
    public DateTime ChangedDate { get; set; }

    [DataMember]
    public byte[] Timestamp { get; set; }

    public abstract void Validate();
}

Next we need to turn off change tracking proxies created for our entities. These would normally be created because we have marked the entities properties as virtual. These proxies cannot be serialized and if they were generated the navigation properties on the POCO objects will not serialize and your response comes back empty resulting in the “The underlying connection was closed: The connection was closed unexpectedly” exception. For turning off change tracking proxies we need to add the below given code to the BaseRepository constructor
public BaseRepository(IUnitOfWork unitOfwork)
{
    if (unitOfwork == default(ShoppingCartContext)) throw new ArgumentNullException("UnitOfWork cannot be null");
    Context = unitOfwork as ShoppingCartContext;
    Context.ContextOptions.LazyLoadingEnabled = false;
    Context.ContextOptions.ProxyCreationEnabled = false;
}

Since Lazy load is disabled, to include the child entities in the model, we need to explicitly specify the include statements for each objects. For e.g  the CustomerRepository GetAll method is changed to
public override IEnumerable<Customer> All()
{
    return Context.Customers
        .Include("Orders")
        .Include("Orders.OrderProducts")
        .Include("Orders.OrderProducts.Product");
}
This will include the Orders, OrderProducts for the orders and Product for each orderProduct in the Orders for the customer.
Now we can try to use the updated model in our service.
[ServiceContract]
public interface IShoppingCartService
{
    [OperationContract]
    Customer GetById(Guid id);
}

public class ShoppingCartService : IShoppingCartService
{
    public ShoppingCartService()
    {
        unitOfWork = UnitOfWork.Default;
    }

    public Customer GetById(Guid id)
    {
        var customerRepository = new CustomerRepository(unitOfWork);
        return customerRepository.GetById(id);
    }

    IUnitOfWork unitOfWork;
}

Test cases
[TestMethod]
public void GetAllOrdersByCustomer_should_retrive_all_orders_placed_by_a_customer()
{
    var proxy = new ShoppingCartServiceClient.ShoppingCartServiceClient("WSHttpBinding_IShoppingCartService");
    var customer = proxy.GetById(new Guid("1931FEDC-2CCA-437E-A3BD-D5842879530D"));
    Assert.IsNotNull(customer);
}

Next we’ll add new methods to the service to perform more operations on the context to add, remove and update properties on the entities.

Tuesday, November 30, 2010

Entity framework creating a Generic Repository and UnitOfWork implementation – Part 2

We have now moved our validation and base entity property change logic to the base repository implementation. So we can now remove the SavingChanges event code from the ObjectContext implementation. The changed code looks like
public class ShoppingCartContext : ObjectContext
{
    public ShoppingCartContext() : base("name=ShoppingCartModelContainer", "ShoppingCartModelContainer") { }

    public override int SaveChanges(SaveOptions options)
    {
        this.DetectChanges();
        return base.SaveChanges(options & ~SaveOptions.DetectChangesBeforeSave);
    }

    public ObjectSet<Customer> Customers
    {
        get { return _customers ?? CreateObjectSet<Customer>(); }
    }

    public ObjectSet<Order> Orders
    {
        get { return _orders ?? CreateObjectSet<Order>(); }
    }

    public ObjectSet<Product> Products
    {
        get { return _products ?? CreateObjectSet<Product>(); }
    }      

    ObjectSet<Customer> _customers;
    ObjectSet<Order> _orders;
    ObjectSet<Product> _products;
}

Once the base repository is ready, we can now create repositories for our entities in the model. I’ll show the code for the customer repository implementation.
public interface ICustomerRepository : IRepository<Customer>
{
    EF4Recipies.Models.Customer GetByEmail(string email);
    System.Collections.Generic.IEnumerableCustomer> SearchByName(string name);
}

public class CustomerRepository : BaseRepository<Customer>, ICustomerRepository
{
    public CustomerRepository(IUnitOfWork unitOfWork) : base(unitOfWork) { }

    public override IEnumerable<Customer> All()
    {
        return Context.Customers;
    }

    public override void Remove(Customer entity)
    {
        Context.Customers.Attach(entity);
        base.Remove(entity);
    }

    public override void Update(Customer entity)
    {
        Context.Customers.Attach(entity);
        base.Update(entity);
    }

    public Customer GetByEmail(string email)
    {
        return base.GetOneByCondition(customer => customer.Email == email);
    }

    public IEnumerable<Customer> SearchByName(string name)
    {
        return base.AllByCondition(customer => string.Concat(customer.FirstName, customer.LastName).Contains(name));
    }
}

Test cases:
[TestMethod()]
public void All_should_return_the_complete_set_of_customers_in_db()
{
    IUnitOfWork unitOfWork = UnitOfWork.Default;
    CustomerRepository target = new CustomerRepository(unitOfWork); 
           
    var customers = target.All();
    Assert.IsTrue(customers.Count() > 0);
}

[TestMethod()]
public void GetByEmail_should_return_customer_with_email_address_passed()
{
    IUnitOfWork unitOfWork = UnitOfWork.Default;
    CustomerRepository target = new CustomerRepository(unitOfWork);
    string email = "prajeesh.prathap@gmail.com";
    var customer = target.GetByEmail(email);
    Assert.AreEqual<string>(email, customer.Email);
}

[TestMethod]
public void Add_method_should_add_customer_to_the_context()
{
    IUnitOfWork unitOfWork = UnitOfWork.Default;
    CustomerRepository target = new CustomerRepository(unitOfWork);
    Customer customer = new Customer
    {
        FirstName = "SampleFName",
        LastName = "SampleLName",
        Email = "ValidEmail@gmail.com"               
    };

    target.Add(customer);
    unitOfWork.Save();

    unitOfWork = UnitOfWork.Default;
    CustomerRepository newRepository = new CustomerRepository(unitOfWork);
    var newCustomer = newRepository.GetByEmail("ValidEmail@gmail.com");
    Assert.IsNotNull(newCustomer);
    unitOfWork.ExecuteCommandOnDb("delete from [dbo].[Customers] where Id = '" + newCustomer.Id.ToString() + "'");           
}


Entity framework creating a Generic Repository and UnitOfWork implementation – Part 1

The repository and unit of work patterns have gained a lot of acknowledgement from the agile development community and have gained lot of popularity in the recent years. In these series of posts I’ll explain how to create a repository and unit of work implementation using entity framework POCO development. I’ll be using the models from the samples used in my previous posts on the topic. In you have missed those, I would recommend to check out these samples before looking into this.
My attempt is to create a base repository class which handles the GRUD operations and entity specific repository implementations have the additional functionality that are specific to business requirements and inherit this base class. The unit of work implementation is borrowed from the concept described in the post by Faisal Mohamood in the ADO.NET team blogs.
Implement Unit Of Work pattern using ObjectContext
Unit of work implementation have two methods Save() and ExecuteCommandOnDb.
The Save method calls the SaveChanges on the context. ExecuteCommandOnDb method is mainly used in the test cases where we need to directly execute a SQL query on the data store.
public class UnitOfWork : ShoppingCartContext, EF4Recipies.Repository.IUnitOfWork
{
    private UnitOfWork() { }

    public static IUnitOfWork Default
    {
        get
        {
            return new UnitOfWork();
        }
    }

    public void Save()
    {
        SaveChanges();
    }

    public void ExecuteCommandOnDb(string sqlCommand)
    {
        this.ExecuteStoreCommand(sqlCommand);
    }
}

Defining the interface for Repositories
The IRepository interface defines the common functionality that can be implemented in the base repository class. It looks like
public interface IRepository where T : BaseEntity
{
    void Add(T entity);
    System.Collections.Generic.IEnumerable All();
    System.Collections.Generic.IEnumerable AllByCondition(Funcbool> predicate);
    T GetById(Guid id);
    T GetOneByCondition(Funcbool> predicate);
    void Remove(T entity);
    void Update(T entity);
}

The base repository class implements this interface.
public abstract class BaseRepository : EF4Recipies.Repository.IRepository where T : BaseEntity
{
    public BaseRepository(IUnitOfWork unitOfwork)
    {
        if (unitOfwork == default(ShoppingCartContext)) throw new ArgumentNullException("UnitOfWork cannot be null");
        Context = unitOfwork as ShoppingCartContext;
    }

    public abstract IEnumerable All();

    public T GetById(Guid id)
    {
        return All().SingleOrDefault(entity => entity.Id == id);
    }

    public T GetOneByCondition(Funcbool> predicate)
    {
        return All().SingleOrDefault(predicate);
    }

    public IEnumerable AllByCondition(Funcbool> predicate)
    {
        return All().Where(predicate);
    }

    public void Add(T entity)
    {
        entity.Id = entity.Id == Guid.Empty ? Guid.NewGuid() : entity.Id;
        entity.CreatedDate = entity.ChangedDate = DateTime.Now;
        ValidateAndAddToObjectSet(entity);
    }

    private void ValidateAndAddToObjectSet(T entity)
    {
        entity.Validate();
        var objectSet = All() as ObjectSet;
        objectSet.Context.AddObject(objectSet.EntitySet.Name, entity);
    }

    public virtual void Remove(T entity)
    {
        Context.DeleteObject(entity);
    }

    public virtual void Update(T entity)
    {
        var entityFromDb = GetById(entity.Id);
        if (entityFromDb != null && StructuralComparisons.StructuralEqualityComparer.Equals(entity.Timestamp, entityFromDb.Timestamp))
        {
            entity.ChangedDate = DateTime.Now;
            ValidateAndAddToObjectSet(entity);
        }
        else
            throw new InvalidOperationException("Concurrency exception");
    }
       
    protected ShoppingCartContext Context { get; private set; }
}

The Add and Update methods calls the validation on the entities that we have implemented in this post.
The update method also checks for concurrency issues before updating by comparing the timestamp field value with the entity object to be saved. Both remove and Update methods are implemented as Virtual because these need to be attached to the context from the class that inherits the base class. We’ll see the implementation of a CustomerRepository using the base repository we implemented in the upcoming posts.