So here's the scenario...My website displays three different types of products for sale. For this example, we'll call them Furniture, Clothes and Toys. The three products are distinctly different, yet all share some of the same properties (Price, DateCreated, Description, ShortDescription, etc).
I'm using EF4 so when I display a list of products on the "Furniture" page, I can just do something like this:
IEnumerable<Furniture> furniture = Repository.GetFurniture();
Everything is working great when I'm only displaying one product type on a page. I have the entire set of Furniture displayed in a paged list of 10 products per page, sortable by Name, Price, Ad Date, etc.
However, I want to create a link to "See all of this seller's products". This page will need to display EVERYTHING the seller has listed regardless of whether it's furniture, clothes or toys. The trick is, it also needs to be sortable by the fields that all of the products share in common (price, name, ad date, etc).
So what I'd like to do is something like this pseudo-code:
IEnumerable<IProduct> products =
Repository.GetAllProducts(sellerId).OrderBy(sortField).Skip(skip).Take(take);
Then, in the View (yes, I'm referencing EF4 entities in the view):
#foreach (var product in Model.products.OrderBy(sortField))
{
if (product is Furniture)
{
Html.RenderPartial("FurnitureResult", (Furniture)product)
}
else if (product is Clothes)
{
Html.RenderPartial("ClothesResult", (Clothes)product)
}
// repeat for toys
}
So my question is:
What would the call to Repository.GetAllProducts(sellerId) look like? It would need to be a set of LINQ queries that gets all of the furniture, clothes and toys and combines them into a single IEnumerable<>. If that's not possible, what other methods could I employ to get a list of products like this?
Essentially I just need to know what the best method is to group a set of unrelated objects with some common properties into a single collection and then sort that list by one of the common properties and then determine which concrete class it is as I iterate over them.
Thanks!
Try to create the partial classes for each of the entities. The partial class will implement the interface IProduct, which will not be a part of EF, but will have the properties to return price, name, ad date etc.
Implement these properties getters to return specific properties of the entity and implement the method GetAllProducts to return the combined enumerable of all entity types.
Have you considered creating a Database View, and adding it to your Context. Otherwise,
Repository.GetAllProducts(sellerId).OrderBy(sortField).Skip(skip).Take(take);
would run in the memory, I mean you'd have to at least execute it on 3 different DbSets, and merge those to get the final set.
Another option is using the Inheritance, make a base Product type, and inherit the 3 different products from it. http://weblogs.asp.net/manavi/archive/2010/12/24/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph.aspx
Check out all Inheritance options from the above web blog, so there are 2 more blogs to follow up in it.
Alternative options:
Union all products in a collecton of type Object so Repository.GetFurniture().Cast().Union(Repository.GetClothes().Cast());
Or: use dynamic and ducktyping and render using common property names. (bleg)
Related
I'm just starting to get my head around LINQ queries. I've looked at the MSDN page about type relationships, and understand the rudiments of querying. However, I can't find anything that helps me with querying when EF6 (code first) has merged a number of concrete classes into one table based on the base class.
I have a base abstract class declared like this:
public abstract class Product
{
// a whole bunch of fields, properties, and methods
}
and then a bunch of concrete classes declared like this:
public class ProductOption : Product
{
public ProductOption()
{
// A bunch of stuff to set properties in the base class.
}
}
Entity Framework creates a single table called Products that has a column named Differentiator with values identifying the name of the concrete class the particular row holds (e.g. ProductOption). The column does not create a property in Product.
What I would like to do is (following the MSDN example) declare something like:
var queryAllProductOptions = from po in ProductOptions
select po;
I've heard from at least one person that I need to filter instead, looking something like this (luckily we have a property that helps):
var queryAllProductOptions = from po in Products
where po.Category = ProductCategory.Option
select po;
The implications of being able to use the first method are significant. We have a workaround, but it might cause complications in certain instances (though none insurmountable).
Does the first way work, or do we need to filter Products the way the second method outlines?
I think what you have here is TPH inheritance.
Querying types in a TPH mapping are like this :
var query =
from lodge in context.Lodgings.OfType<Resort>()
select lodge;
excerpt coming from programming entity framework page 392
So did you consider the OfType yet ?
I know that this is a repeated question and I know that this is not possible if there are additional properties in the "in the middle" table.
I had an idea how to get the effect of an m:N relationship instead of an 1:n-n-1, but I'd like to hear some other thoughts.
If I have three entities, A, B, and AB where AB makes the A:B relation possible and it has additional properties.
Using Databasefirst approach, I thought to make a partial class of A and B.
public partial Class A
{
public IEnumerable<EntityObject> Bs
{
get
{
return this.Select(p=>p.AB.B);
}
set { //... }
}
}
Could something like this be possible.
Just doodling in my head. I am currently on vacation and have no computer, so this is not tested but just written on my cell phone.
I see that this could be a problem after context disposing or detaching, also with including in an eager loading approach.
Any thoughts?
If you are already treating AB as a distinct entity, then to get all B from A all you need is something like this:
public partial class A
{
public IQueryable<B> Bs {
get { return this.ABs.AsQueryable().Select(ab => ab.B).Distinct(); }
}
}
I'm not sure how well this will perform, as compared to a built-in Many-To-Many supported by EF (without any payload), but it will give you what you are asking.
If technically possible or not, expressing such a relationship with "additional properties in the in the middle table" as many-to-many relationship is just wrong because it hides that the "middle table" has a business meaning and therefore must be an entity on its own.
A somewhat classical example for such a model are RawMaterial and Product: A RawMaterial can be used in multiple Products and a Product can be made of multiple RawMaterials. The entity in between - maybe called RecipePart - contains a Quantity how many pieces of a given RawMaterial are used in a given Product.
If you have for example the product ChocolateBar and work with its relation to raw materials you will deal with a recipe that says a ChocolateBar has 60 units of Chocolate and 40 units of Milk, i.e. ChocolateBar has a collection of RecipeParts and every RecipePart describes the quantity and refers to the related RawMaterial. A ChocolateBar does not have a direct collection of RawMaterials in this business model.
For a particular query (maybe some statistics) you might be only interested in its raw materials - a chocolate bar is made if chocolate and milk, no matter how many units - but that is a special query in your business model and kind of an aggregation that ignores some pieces of the full detailed model information. This is what your helper property this.Select(p=>p.AB.B); does: It does not express the full relationship but is a specialized query that says: Give me only the RawMaterials for this Product, I don't want to know each quantity.
Characteristically you have left the property setter set { //... } a stub. When adding or changing entities it becomes obvious that the relationship cannot be many-to-many. It is not possible to assign only a list of RawMaterials to a Product. You must add the information how many units of each RawMaterial to get a valid Product model which means that Product must be related to the "middle entity" RecipePart.
I am working on a CMS using ASP.NET / C#. It works fine and life is good. However, it was decided to add extra functionality to support a wider variety of websites.
Basically, the current CMS is still alpha but will be able to host data for multiple websites running for the same group of companies all having somewhat the same requirements.
I have constructed the database as per the diagram attached below:
"Image removed for security reasons"
Now, in addition to this diagram, I was asked to add support for products under different 'attributes' than Sectors. Meaning that a product now can have a type and many other attributes. So let's say you're using one of our sites and we make tissue paper. Products should somehow support filtering by 'type' say 'tissue' and by 'application' say 'how you use it' and other unknown 'attributes' that may pop up in the future.
For example a product may fall under the 'Agricultural Packaging' sector and is of type 'Bag' and is applied by some way of applying it.
The end result is to be able to sort products by how they are used and or their type and or the sector they fall under. Or even all together.
What is the best approach for this sort of problem to include into the data model and the CMS?
Thanks!
It looks like you're taking the right approach. If a product can be assigned more than one of the new 'attributes', then you'll want to create a new Attributes table where you can define a finite set of attributes. Then link that table to Products via a cross-reference table, just like you're doing with Products & Sectors. If a Product can only be assigned a singe attribute, then add the Attributes table, but instead of using a cross-reference table, add an AttributeID column to the Products table to join the two.
I feel like I may be telling you something that you already know, however. So if there is any additional information, or more specifics you want, maybe you can post an update.
The pattern you are looking for is called EAV
You will end up with an attribute table and an attribute value table that has the product id, attribute id, and attribute value in in it.
I use C# to write a (hypothetical) application - online store.
I have a database of products, each product has following information associated with it:
Product Number (required)
Name (required)
Price (required)
Rating (optional)
Sold Quantity (optional) -- this is total sale of this product
I have 4 pages that show filtered list of products. Pages show different information for each product:
1st page : PN, Name, Price
2nd page : PN, Name, Price, Rating
3d page : PN, Name, Price, Sold Quantity
4th page : PN, Name, Price, Rating, Sold Quantity
My question is, how do I design data structures to accommodate all my pages with little duplication?
Brute force approach is to create a type for each page:
IList<Product>
IList<ProductWithRating>
IList<ProductWithSoldQuantity>
IList<ProductWithRatingAndSoldQuantity>
later 3 can derive from Product but due to lack of multiple inheritance ProductWithRatingAndSoldQuantity can't derive from both Rating and SoldQuantity products.
In a dynamic language I would just add whatever fields I need and be happy.
So I could simulate a dynamic-language-approach by storing extra information (rating, sold quantities) in separate dictonaries, e.g.:
{
IList<Product> Products;
IDictionary<Product, Rating> ProductRatings;
IDictionary<Product, SoldQuantity> ProductSoldQuantities;
}
// is equivalent to
IList<ProductWithRatingAndSoldQuantities>
Building a Product structure that includes everything and then pass around a partially initialized object is not a solution I am looking for.
Any suggestions?
Sorry not enough chars to reply in comment.
You should have a single domain object, Product. It would have non-nullable Name and ProductNumber and Price, because you cannot have products that don't have those things.
Rating should be a nullable, because it is possible to have a Product that does not have a rating. Whether or not a Product has a Rating, it is still always a Product. I'll leave QuantitySold, because I wouldn't actually store that as a property of product, I would have Orders and OrderLine collections, and calculate QuantitySold from those (normalisation). However in the absence of those other collections, you could store it as a field on Product. If I was going to do that, it would be a non-nullable integer property, the default being zero.
You only need a single collection to filter, which would be some implementation of IEnumerable or IQueryable or both, most likely you would opt for something like Entity Framework and actually have an ObjectSet but I'd try to keep my design agnostic of what storage method I'm using and work against those interfaces.
You can then query your single collection to identify which attributes are null on the Products in your domain model. Syntax might not be perfect, but nothing Intellisense won't pick up, I'm a VB guy 99% of the time.
var productsWithNoSales = Context.Products.Where(p=> p.QuantitySold == 0);
var productsWithNoRating = Context.Products.Where(p=> p.Rating == nothing);
var productsWithNoSalesOrRating = Context.Products.Where(p=> p.QuantitySold == 0).Where(p=> p.Rating == nothing);
That is pretty much the cleanest possible domain model for what you are after.
Inheritance would be if you had specialised derivatives of Product that have either extra properties or different behaviour. For example, my own system has a base Product class, and a EbayProduct and AmazonProduct entity which both inherit from Product and contain only the extra logic and properties associated with working with those sites. My Product class has about 20 properties - mostly nullable, as when we list products we don't necessarily have all the information available. Of these 20, the most I display on any one page is about 15. I probably do something similar to what you are trying to do in that I filter out Products that aren't ready to list yet using the exact method described, i.e. filtering my Products collection for missing fields.
Why can't Rating and SoldQuantity just be nullable? Or be always present but just not always displayed?
Can you store the rating and quantity sold as attributes, and then store booleans that indicate whether they're present? I think you're using inheritance when you should probably be doing composition.
You could perhaps do with specifying what you're using? What are your "pages" in for a start? What is your data storage method?
It sounds like you are muddling displaying your data with storing your data? There is no obligation to show data just because it exists.
Your object domain should almost certainly just have products. The data storage would be set up such that the properties are nullable, and check for nulls to get the data back.
If you're using something like Linq, you can simply do something like
var productsWithoutRating = Context.Products.Where(p => p.Rating == nothing);
The dictionary idea, inheritance, and composition all seem a bit wierd. Storing a separate boolean telling you whether a property exists is messy, and would be a nightmare to maintain - just check whether it exists on the fly.
I'm thinking of building a ecommerce application with an extensible data model using NHibernate and Fluent NHibernate. By having an extensible data model, I have the ability to define a Product entity, and allow a user in the application to extend it with new fields/properties with different data types including custom data types.
Example:
Product can have an addition fields like:
Size - int
Color - string
Price - decimal
Collection of ColoredImage - name, image (e.g. "Red", red.jpg (binary file))
An additional requirement is to be able to filter the products by these additional/extended fields. How should I implement this?
Thanks in advance.
I think this link describes kind of what you want...
http://ayende.com/Blog/archive/2009/04/11/nhibernate-mapping-ltdynamic-componentgt.aspx
More info on dynamic-component:
http://www.mattfreeman.co.uk/2009/01/nhibernate-mapping-with-dynamic-component/
http://bartreyserhove.blogspot.com/2008/02/dynamic-domain-mode-using-nhibernate.html
The idea behind dynamic-component is that you can build your data model by not having a one to one mapping of databse columns with properties. Instead you have only a dictionary property that can contain data from as many properties as you like. This way when you fetch the entity, the dictionary gets the data of all columns configured to belong in there. You can extend the database table's schema to include more columns and that will be reflected to the databse model if you update the mapping file accordingly (manually or though code at application start).
To be honest I do not know you can query such entity using the "attributes" property but if I had to guess I would do an IN statement to it.
One of the options is EAV model (Entity-Attribute-Value).
This model is good to apply if you have a single class in your domain, which table representation would result in a wide table (large number of columns, many null values)
It's originally designed for medical domain, where objects may have thousands of columns (sympthoms).
Basically you have
Entity (Id) (for example your Product table)
Attribute(Id, ColumnName)
Value(EntityId, AttributeId, value)
You can have some additional metadata tables.
Value should better be multiple tables, one for a type.
For example:
ShortStringValue(EntityId, AttributeId, Value nvarchar(50));
LongStringValue(EntityId, AttributeId, Value nvarchar(2048));
MemoValue(EntityId, AttributeId, Value nvarchar(max));
IntValue(EntityId, AttributeId, Value int);
or even a comple type:
ColorComponentsValue(EntityId, AttributeId, R int, G int, B int );
One of the things from my experience is that you should not have EAV for everything. Just have EAV for a single class, Product for example.
If you have to use extensibility for different base classes, let it be a separate set of EAV tables.
Onother thing is that you have to invent a smart materialization strategy for your objects.
Do not pivot these values to a wide row set, pivot just a small number of collumns for your query criteria needs, then return a narrow collection of Value rows for each of the selected objects. Otherwise pivoting would involve massive join.
There are some points to consider:
. Each value takes storage space for foreign keys
. For example row-level locking will behave different for such queries, which may result in performance degradation.
. May result in larger index sizes.
Actually in a shallow hellow world test my EAV solution outperformed it's static counterpart on a 20 column table in a query with 4 columns involved in criteria.
Possible option would be to store all extra fields in an XML structure and use XPath/XQuery to retrieve them from the database.
Each extensible entity in your application will have an XML field, like ExtendedData, which will contain all extra properties.
Another option is to use Non-relationnal Databases which are typically suited for this kind of things.
NOSQL databases(couchDB, mongoDB, cassandre...) let you define dynamically your propretyfields, you could add fields to your product class whenever you want.
I'm searching for similar thing and just found N2 CMS (http://n2cms.com) which implements domain extensibility in quite usable way. It also supports querying over extension fields which is important. The only downside I find out is that it's implemented using HQL so it would take some time to reimplement it to be able to query using QueryOver/Linq, but the main idea and mappings are there. Take a look on ContentItem, DetailCollection, ContentDetail classes, their mappings and QueryBuilder/DetailCriteria.