I have a product class which contains 11 public fields.
ProductId
ShortTitle
LongTitle
Description
Price
Length
Width
Depth
Material
Img
Colors
Pattern
The number of fields may grow with attributes for more specific product tyes. The description may contain a large amount of data.
I want to create a slim version of this product class that only contains the data needed. I'm only displaying 4 of the 12 fields when listing products on a category page. It seems like a waste to retrieve all of the data when most of it isn't being used.
I created a parent class of ProductListing that contains the 4 fields I need for the category page
ProductId
ShortTitle
Price
Img
Then created a class of Product that inherits from ProductListing containing all product data. It seems backwards as "ProductListing" is not a kind of "Product" but I just started reading about inheritance a few months ago so it's stil a little new to me.
Is there a better way to get a slim object so I'm not pulling data I don't need?
Is the solution I have in place fine how it is?
I personally do not favor inheritance for these kinds of problems because it can become confusing over time. Specifically, I try to avoid having two concrete classes in my inheritance hierarchy where one inherits from the other and both can be instantiated and used.
How about creating a ProductCoreDetail class that has the essential fields you need and aggregating it inside of the Product class. You can still expose the public fields by declaring them as public fields and proxying them to the nested ProductCoreDetail instance.
The benefit of this model is that any shared implementation code can be placed in ProductCoreDetail. Also, you can choose to define an additional interface IProductCoreDetail that both Product and ProductCoreDetail implement so that you can pass either instance to methods that just care about code information. I would also never exposed the aggregate instance publicly to consumer of Product.
Here's a code example:
// interface that allows functional polymorphism
public interface IProductCoreDetail
{
public int ProductId { get; set; }
public string ShortTitle { get; set; }
public decimal Price { get; set; }
public string Img { get; set; }
}
// class used for lightweight operations
public class ProductCoreDetail : IProductCoreDetail
{
// these would be implemented here..
public int ProductId { get; set; }
public string ShortTitle { get; set; }
public decimal Price { get; set; }
public string Img { get; set; }
// any additional methods needed...
}
public class Product : IProductCoreDetail
{
private readonly ProductCoreDetail m_CoreDetail;
public int ProductId { get { return m_CoreDetail.ProductId; } }
public string ShortTitle { get { return m_CoreDetail.ShortTitle; } }
public decimal Price { get { return m_CoreDetail.Price; } }
public string Img { get { return m_CoreDetail.Img; } }
// other fields...
public string LongTitle
public string Description
public int Length
public int Width
public int Depth
public int Material
public int Colors
public int Pattern
}
I agree with LBushkin that inheritence is the wrong approach here. Inheritence suggests that TypeB is a TypeA. In your case, the relationship is not quite the same. I used to create classes that were subsets of a large entity for things like search results, list box items, etc. But now with C# 3.5's anonymous type support and LINQ projections, I rarely need to do that anymore.
// C#
var results = from p in products
select new {
p.ProductId,
p.ShortTitle,
p.Price,
p.Img };
// VB.NET
Dim results = From p in products _
Select p.ProductId, p.ShortTitle, p.Price, p.Img
This creates an unnamed type "on-the-fly" that contains only the fields you specified. It is immutable so the fields cannot be changed via this "mini" class but it supports equality and comparison.
But when I do need to create a named type, I typically just create a separate class that has no relationship to the main class other than a lazy-loaded reference to the "full" version of the object.
I wouldn't use a separate class or inheritance.
For your slim version, why not just retrieve only the data you need, and leave the other fields empty? You might have two queries: one that fills all the fields, and another that only fills the slim fields. If you need to differentiate between the two, that's easy if one of the non-slim fields is NOT NULL in your DB; just check for null in the object.
Related
Good Day Sir / Ma'am,
I have a question regarding extending graphs in Acumatica.
I have extended the SalesOrderEntry Graph with 2 custom views namely ReservationDetails and PropertyItems. Everything is running well except when I try to fetch a record, the details on my PropertyItems view are not populating.
EXTENDED GRAPH
public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
{
#region Selects
public PXSelect<RECOReservationDetail,
Where<RECOReservationDetail.reservationNbr,
Equal<Current<SOOrder.orderNbr>>>> ReservationDetails;
public PXSelectJoin<InventoryItem,
LeftJoin<RECOReservationDetail, On<InventoryItem.inventoryID,
Equal<RECOReservationDetail.inventoryID>,
And<RECOReservationDetail.reservationNbr,
Equal<Current<SOOrder.orderNbr>>>>>,
Where<InventoryItem.inventoryID,
Equal<Current<RECOReservationDetail.inventoryID>>>> PropertyItems;
CUSTOM TABLE - ReservationDetail DAC
namespace RealEstate.DAC.CO
{
[Serializable]
public class RECOReservationDetail : IBqlTable
{
#region Reservation Nbr.
[PXDBString(15, IsKey = true)]
[PXUIField(DisplayName = "Reservation Nbr.")]
[PXParent(typeof(Select<SOOrder,
Where<SOOrder.orderNbr,
Equal<Current<RECOReservationDetail.reservationNbr>>>>))]
[PXDBDefault(typeof(SOOrder.orderNbr))]
public virtual string ReservationNbr { get; set; }
public abstract class reservationNbr : IBqlField { }
#endregion
#region Branch ID
[PXDBInt]
[PXSelector(typeof(Search<Branch.branchID>),
SubstituteKey = typeof(Branch.branchCD))]
[PXUIField(DisplayName = "Branch ID", Required = true)]
[PXDefault(typeof(AccessInfo.branchID), PersistingCheck = PXPersistingCheck.Nothing)]
public virtual int? BranchID { get; set; }
public abstract class branchID : IBqlField { }
#endregion
#region Inventory ID
[StockItem]
[PXUIField(DisplayName = "Inventory ID")]
public virtual int? InventoryID { get; set; }
public abstract class inventoryID : IBqlField { }
#endregion
Page - Image
The above image is the view when I'm trying to fetch an order from sales order. As you can tell, It populates the document details part except for the Features Group. I already put CommitChanges = True on the Inventory ID field so that it will fill in the necessary information for the features part, but sadly it doesn't fill in any data.
<px:PXSegmentMask ID="edInventoryID" runat="server" CommitChanges="True" DataField="InventoryID"></px:PXSegmentMask>
I tried debugging it, but the PropertyItems view always returns null value.
Thank you so much for the replies.
UPDATE - 10/05/2018
Full Page Link
Full Extended Graph Link
Full DAC Link
I think your main problem occurs because you re-implemented the Sales Order screen instead of extending it. While doing so you removed some important elements like the Document Details 'grid'.
If I just add your 2 Data Views to the original Sales Order screen without removing anything they appear to sync better. Notice it does pickup the Item description and item image properly (other blank fields is because I miss your custom DAC/Table):
To test this I extended original Sales Order instead of creating a new screen that is a copy of the orginal Sales Order. You likely removed too much of the original screen.
I have found many tutorials that use HashSet ex
this.Supplier = new HashSet<supplier>();
In many-to-many relation. But som tutorials use the code below without HashSet (no more or less)
public partial class Product
{
public Product()
{
this.Supplier = new HashSet<supplier>();
}
public long ProductID { get; set; }
public string ProductName { get; set; }
//navigation property to Supplier
public virtual ICollection<supplier> Supplier { get; set; }
}
public partial class Supplier
{
public Supplier()
{
this.Product = new HashSet<product>();
}
public long SupplierID { get; set; }
public string SupplierName { get; set; }
// navigation property to Product
public virtual ICollection<product> Product { get; set; }
}
When I tested the code above and deleted
public xxxx()
{
this.xxxx = new HashSet<xxxx>();
}
I still got an association table and a many-to-many relation.
Why do I need HashSet?
Usually many-to-many relationship defined with ICollection in both table models:
public virtual ICollection<supplier> Supplier { get; set; }
public virtual ICollection<product> Product { get; set; }
The presence of ICollection on models means that lazy loading is enabled, allowing EF to create derived classes for them.
About the use of HashSet inside model generations, ChrisV said in HashSet in EF many to many:
HashSet implements a hash table that is very efficient for a lot of
operations, for instance searching a large set for a single item.
The usage of HashSet by default primarily based on efficiency reasons besides of non-null value, such like Yuval Itzchakov said in Entity Framework:Why the collection type of entity class need to be instanced in the default constructor?:
A HashSet is used because it guarantees that two values which are
equal to each other (which are equality checked by looking at their
GetHashCode and Equals methods) only appear once in the collection.
And yes, you can change the concrete type to any type which implements
ICollection<T>.
The explanations above can be summarized as "HashSet is initialization step of ICollection interface inside model's constructor which guarantees equality between each related model members". EF itself doesn't care what implementations should be apply on ICollection for table models, you can use List<T> in constructor replacing HashSet<T> and many-to-many relationship still doesn't affected.
Inside your template model (.tt file), you may see these lines to generate HashSet<T> by default as it implements ICollection:
foreach (var navigationProperty in collectionNavigationProperties)
{
#>
this.<#=code.Escape(navigationProperty)#> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>();
<#
}
You may doing experiments by removing or changing HashSet<T> initialization when the model is re-generated, however I considered not a good practice to remove it for large amount of data operations.
I am having difficulties solving this problem using RavenDB. I have these classes.
I excluded a lot of properties from them to keep the example simple.
public class Menu
{
public string Name { get; set; }
public List<NavigationNode> Nodes { get; set; }
}
public class NavigationNode
{
public string Text { get; set; }
public Guid? PageId { get; set; }
public string NodeType { get; set; }
public List<NavigationNode> Nodes { get; set; }
}
public class Page
{
public Guid PageId { get; set; }
public string Slug { get; set; }
}
So as you can see this is about rendering a navigation menu. Node lists are hierarchical and can go deep, in theory infinitely(of course in reality 2-4 sub levels only). At first i stored the Slug in the Node but realised that, what happens if the page Slug changes, changing all nodes when a page changes slug i would have to loop through all menus, climb down the hierarchy and find all those Slug values to change them, which does not sound like an optimal solution.
So i was thinking it should be possible to build an index that combines the Page Slug with rest of the data from the Node in a hierarchical structure.
I have been reading about Map Reduce, Multimap and recurse but i am not even sure where to start.
I found this http://ravendb.net/docs/2.0/client-api/querying/static-indexes/indexing-hierarchies
Here is a simple example i tried just to get something started, and i cannot even get that to work since i really don't understand the example on the page i linked to above.
public class NavigationIndex : AbstractIndexCreationTask<Menu>
{
public NavigationIndex()
{
Map = menus => from menu in menus
from node in Recurse(menu, x => x.Nodes)
select new
{
WhatIsThis = node // <- Why is this a collection?
};
}
}
According to the example node should not be a collection but an actual NavigationNode object.
Is it possible to achieve what i want in RavenDB and what am i doing wrong in the example?
Please feel free to ask anything you find confusing.
I apologize for the confusion. I'll try to explain it.
EDIT:
Changing PageId to string will not be a problem. I am using Guids since i need to be able to generate primary key ID:s before inserting. Anyway, what i want to Query for from the index is a hiearachical tree of navigation links where the Pages Slug is included. So i can recursively loop out a navigation menu on the website.
Answers to Matt Johnson's questions:
I want a output of a class you can see below
Page is a separate document
I am only going to query by Menu Name
public class NavigationIndexItem{
public string MenuName { get; set; }
public string Text { get; set; }
public string Slug { get; set; }
public string NodeType { get; set; }
public List ChildItems { get; set; }
}
Now when i see the above class i think i might a bit on the wrong path.
But anyway i will make some minor changes and thank you Matt for answering. However i am still running into same problem as before.
This row in your example: where node.PageId != null
node is not an instance of a specific NavigationNode but yet another collection so i cannot check the PageId property on it. I only get a list of LINQ extensions.
I'm making some assumptions of what you want. See my comments on the original question. But I think this is what you are after.
First - you need to change the id of the Page class to Id instead of PageId. This is so Raven will use your guid as part of its document id. Really you would be better off with string Ids, but this will still work.
Then you can do the following:
public class NavigationIndex : AbstractIndexCreationTask<Menu>
{
public NavigationIndex()
{
Map = menus => from menu in menus
from node in Recurse(menu, x => x.Nodes)
where node.PageId != null
let page = LoadDocument<Page>("pages/" + node.PageId)
select new
{
menu.Name,
node.Text,
node.PageId,
page.Slug
};
}
}
This uses the new LoadDocument feature of RavenDB 2.0, which is much more suitable than multi-map for your scenario.
Change it so it would be:
from node in Recurse(menu, x => x.Nodes.AsEnumerable())
I have the following classes:
public class CartItem
{
public long Id { get; set; }
public int Quantity { get; set; }
public Product Product { get; set; }
}
public class Product {
public long Id { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
}
I currently have the following configuration:
modelBuilder.Entity<CartItem>().HasRequired(x => x.Product).WithMany().Map(x => x.MapKey("ProductId"));
I am trying to ensure that whenever I retrieve a cartitem from the database there will be a join on the product table so I can access the product properties but not the other way around.
I basically want to be able to do:
string title = cartItem.Product.Title
using the configuration I have gives me an Object reference not set to an instance of an object exception.
Short answer: to solve your problem, make the Product property virtual.
In-depth:
First, you don't need a join to do this. EF works fine with lazy loading (you need the virtual modifier)
Second, you can fetch the Product eagerly, using the Include extension method. Example:
var cartItem = context.CartItems.Include(x => x.Product)
.Where(/*some condition*/).ToList();
...but you can't configure this to be the default behavior (nor is it a good idea usually)
Third, this is a many-to-one relationship, not one-to-one (a Product has many related CartItems)
What’s Automapper for?
How will it help me with my domain and controller layers (asp.net mvc)?
Maybe an example will help here...
Let's say you have a nicely-normalized database schema like this:
Orders (OrderID, CustomerID, OrderDate)
Customers (CustomerID, Name)
OrderDetails (OrderDetID, OrderID, ProductID, Qty)
Products (ProductID, ProductName, UnitPrice)
And let's say you're using a nice O/R mapper that hands you back a well-organized domain model:
OrderDetail
+--ID
+--Order
|--+--Date
|--+--Customer
|-----+--ID
|-----+--Name
+--Product
|--+--ID
|--+--Name
|--+--UnitPrice
+--Qty
Now you're given a requirement to display everything that's been ordered in the last month. You want to bind this to a flat grid, so you dutifully write a flat class to bind:
public class OrderDetailDto
{
public int ID { get; set; }
public DateTime OrderDate { get; set; }
public int OrderCustomerID { get; set; }
public string OrderCustomerName { get; set; }
public int ProductID { get; set; }
public string ProductName { get; set; }
public Decimal ProductUnitPrice { get; set; }
public int Qty { get; set; }
public Decimal TotalPrice
{
get { return ProductUnitPrice * Qty; }
}
}
That was pretty painless so far, but what now? How do we turn a bunch of OrderDetails into a bunch of OrderDetailDtos for data binding?
You might put a constructor on OrderDto that takes an OrderDetail, and write a big mess of mapping code. Or you might have a static conversion class somewhere. Or, you could use AutoMapper, and write this instead:
Mapper.CreateMap<OrderDetail, OrderDetailDto>();
OrderDetailDto[] items =
Mapper.Map<OrderDetail[], OrderDetailDto[]>(orderDetails);
GridView1.DataSource = items;
There. We've just taken what would otherwise have been a disgusting mess of pointless mapping code and reduced it into three lines (really just two for the actual mapping).
Does that help explain the purpose?
If you have an object of one type and you want to populate the properties of an object of another type using properties from the first type, you have two choices:
Manually write code to do such a mapping.
Use a tool that will automatically handle this for you.
AutoMapper is an example of 2.
The most common use is to flatten models into a data transfer objects (or, in general, mapping across layer boundaries). What's very nice about AutoMapper is that for common scenarios you don't have to do any configuring (convention over configuration).
Map objects between layers. Good example: Here