Reputation: 1795
As a developer, I'm trying to make my classes more modular and re-usable. One of the areas where I've run into problems is when I design a class to work with entity framework entities.
For example, I might create a shopping cart class, who's role is to act as a container for products, discounts, and other things related to checkout. It exposes methods to get the cart total, and enforce some business rules related to what a cart can and can't do.
I might create and entity-framework entity that's a "Product" and make my shopping cart accept this entity. However convenient this seems, it creates an unwanted dependency on the Product class in the project that I'm using the shopping cart in. If I wanted to re-use that shopping cart into another project, the "Product" entity wouldn't necessarily be the same.
How can I elegantly decouple my shopping cart class from entity framework in a way that isn't going to cause architectural problems down the road?
I've thought of creating an interface IProduct that has the related properties and methods that the shopping card would need and then make my entity class an instance of IProduct.
Basically, I'm looking for a good design pattern to decouple my classes from a project's local domain entities, whatever form they might take..
Here's what I ended up going with; a generic shopping cart where T is IProduct. I then make my entity framework entity an IProduct:
public interface IProduct
{
/// <summary>
/// Unique Identifier For the product.
/// </summary>
int Id { get; set; }
decimal Price { get; set; }
}
public interface IShoppingCart<T> where T : IProduct
{
/// <summary>
/// Returns the total of all the products without any modifiers.
/// </summary>
decimal SubTotal { get; }
/// <summary>
/// Returns a total of everything in the cart, including all applicable promotions.
/// </summary>
decimal Total { get; }
/// <summary>
/// Returns a total of the discounts for the applicable promotions in the cart.
/// </summary>
decimal TotalDiscount { get; }
/// <summary>
/// Returns a count of products in the cart.
/// </summary>
int ProductCount { get; }
/// <summary>
/// Returns a count of unique products in the cart.
/// </summary>
int UniqueProductCount { get; }
/// <summary>
/// Adds a product increasing the product count if there is already one in the cart.
/// </summary>
/// <param name="product">Product To Add</param>
void AddProduct(T product);
/// <summary>
/// Remove an instance of a product from the cart, return the product that was removed.
/// </summary>
/// <param name="id"></param>
/// <returns>Instance of T</returns>
void RemoveProduct(int id);
/// <summary>
/// Returns a list of the products in the cart.
/// </summary>
/// <returns></returns>
IList<T> ListProducts();
/// <summary>
/// Remove all products from the cart;
/// </summary>
void ClearAllProducts();
/// <summary>
/// Add a promotion strategy to the cart.
/// </summary>
/// <param name="promotion"></param>
void AddPromotion(IPromotion<T> promotion);
/// <summary>
/// Remove a promotion from the cart.
/// </summary>
/// <param name="promotion"></param>
void RemovePromotion(string key);
/// <summary>
/// Remove all promotions from the cart.
/// </summary>
void ClearAllPromotions();
/// <summary>
/// List all of the promotions currently in the cart.
/// </summary>
/// <returns></returns>
IList<IPromotion<T>> ListPromotions();
/// <summary>
/// Remove everything from the cart (promotions and all).
/// </summary>
void EmptyCart();
}
Upvotes: 1
Views: 248
Reputation: 2368
One approach would be to have two sets of classes with one representing your POCO classes and another representing the Entity Framework classes. Your POCO classes would be Cart, Product, etc, and will contain all the business logic and when you want to write a Product, for example, to the database you would convert the Product POCO class into an Entity Framework Product class either manually or a framework such as AutoMapper.
As an idea you could have a class that will wrap around an Entity Framework data context and will expose methods such as AddProduct which will accept an instance of the POCO class. This method would then convert it to a Entity Framework Product instance and write it to the database.
Downside to this approach is you have to convert a POCO instance to an Entity Framework instance, but it will allow you to use your POCO classes separately from the Entity Framework classes. Plus, you can define your POCO without thinking about the database schema. When you call AddProduct, and perform the conversion from POCO to Entity Framework, you can decide at that point how you want to write the POCO instance to the database.
EDIT: Another approach would be to use Code-First as suggested by @Dmitry S, but Code-First promotes the Convention-over-Configuration approach which can force certain design ideas when you create your POCO classes.
Upvotes: 1
Reputation: 69280
You're on the right track with the idea of an IProduct
interface.
I would place the reusable shopping cart in an own assembly - one that is unaware of persistence (such as Entity Framework). The shopping cart assembly would contain an IProduct
interface that the product of your application would have to implement. This way the shopping cart logic can set requirements on the application that uses it (through the interfaces).
If you need to persist the shopping cart you have to put that persistence logic in the domain layer. One option might be to use the shopping cart as a code first entity, another is to map the data onto an entity class that is part of your Db model.
Upvotes: 1
Reputation: 8513
You can use the "Code First" capability of Entity Framework. It allows you to create domain classes using code and map them to db tables. You can have methods, additional properties, etc in those classes.
http://msdn.microsoft.com/en-us/data/gg685467.aspx
There are a few limitations like each class must have a default constructor and association properties must be virtual. But they are not that problematic.
Upvotes: 1