How to setup polymorphic classes and recursion with C#? - c#-4.0

A base class, VideoContainer, contains a list of VideoContainers.
The properties in VideoContainer are common to three other classes of which there are three:
Layout
Perspective
Source
Each of these classes have different properties, and should fit inside the VideoContainers collection.
/// <summary>
/// Video container
/// </summary>
public class VideoContainer<T>
{
/// <summary>
/// Container ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// Type of container - {Layout, Perspective, SourceContainer}
///
/// This is usually set by the instantiated class.
/// </summary>
public ContainerTypes ContainerType { get; set; }
/// <summary>
/// Parent ID
/// </summary>
public VideoContainerIdentifier ParentObject { get; set; }
/// <summary>
/// Name of container
/// </summary>
public string Name { get; set; }
/// <summary>
/// Details about the physical location of this container
/// </summary>
public LocationDefinition LocationDefinition { get; set; }
/// <summary>
/// When container has a tile applied - number of rows of containers within this perspective
/// </summary>
public short NumRows { get; set; }
/// <summary>
/// When container has a tile aplpied - the number of columns of containers within this perspective
/// </summary>
public short NumColumns { get; set; }
/// <summary>
/// List of containers
/// </summary>
public IList<VideoContainer<T>> VideoContainers { get; set; }
/// <summary>
/// Draw
/// </summary>
public virtual void Draw()
{
// drawing tasks
}
}
The initial problem was that I couldn't put a Layout (or other class type) inside the VideoContainers collection, as it expects a type of VideoContainer.
So I added <T>, hoping but having problems accessing the properties of type <T> - which doesn't work.
How can I set this up properly?
-- UPDATE --
What I forgot to mention is that the classes all inherit VideoContainers.
As per the suggestion below, I created public interface IVideoContainer<T>.
The Layout class is now defined as public class Layout : IVideoContainer<Layout> and implements all of the interface's methods:
public class Layout : IVideoContainer<Layout>
{
/// <summary>
/// ctor
/// </summary>
public Layout()
{
ContainerType = ContainerTypes.Layout;
}
public int Id
{...
Problem is in implementing:
var layout = new IVideoContainer<Layout>
{
Id = 1,
ParentObject = null,
Name = "Layout Definition 1",
LocationDefinition = new LocationDefinition
{
TopLeftX = 0,
TopLeftY = 0,
WidthPixels = 1000,
HeightPixels = 1000
},
NumRows = 20,
NumColumns = 20,
VideoContainers = new List<Perspective>
{
new IVideoContainer<Perspective>
{
Id = 10, ...
-- Update 2 --
I now have:
/// <summary>
/// VideoContainer
/// </summary>
/// <typeparam name="T"></typeparam>
public class VideoContainer<T> : IVideoContainer
{
public int Id { get; set; }
public ContainerTypes ContainerType { get; set; }
public VideoContainerIdentifier ParentObject { get; set; }
public string Name { get; set; }
public LocationDefinition LocationDefinition { get; set; }
public short NumRows { get; set; }
public short NumColumns { get; set; }
public IList<IVideoContainer> VideoContainers { get; set; }
}
The issue is that SourceContainer contains new properties, which I cannot access - CctvId and StreamUri:
VideoContainers = new List<VideoContainer<SourceContainer>>
{
new VideoContainer<SourceContainer>
{
Id = 20,
ParentObject = new VideoContainerIdentifier
{
Id = 10,
ContainerType = ContainerTypes.Perspective
},
ContainerType = ContainerTypes.SourceContainer,
CctvId = new Guid(),
StreamUri = new Uri("http://127.0.0.1/somestream"),
LocationDefinition = new LocationDefinition // TODO: verify that {x,y} are relative to the perspective
{
TopLeftX = 0,
TopLeftY = 0,
WidthPixels = 10,
HeightPixels = 10
}
},
SourceContainer class:
public class SourceContainer : IVideoContainer
{
/// <summary>
/// the URI of the stream for this source
/// </summary>
public Uri StreamUri { get; set; }
/// <summary>
/// the descriptive name of this source
/// </summary>
//public string Name { get; set; }
/// <summary>
/// optional device id for this source
/// </summary>
public Guid? CctvId { get; set; }
/// <summary>
/// ctor
/// </summary>
public SourceContainer()
{
ContainerType = ContainerTypes.SourceContainer;
}
public int Id { get; set; }
public ContainerTypes ContainerType { get; set; }
public VideoContainerIdentifier ParentObject { get; set; }
public string Name { get; set; }
public LocationDefinition LocationDefinition { get; set; }
public short NumRows { get; set; }
public short NumColumns { get; set; }
public IList<IVideoContainer> VideoContainers { get; set; }
}

Create an Interface that collects the common properties, have your three classes inherit from it, and then cast your T to the Interface to access the properties.
public class VideoContainer: IVideoContainer
{
public List<IVideoContainer> Children { get; set; }
}
If you want to access properties specific to the object you're storing in the List, just cast it to the original object:
var child = Children.First();
var type = m.GetType();
if(type.Name == "ChildClass")
{
var container = (ChildClass)child;
// Now you can access VideoContainer specific properties in `container`.
}

Related

ORMLite Service stack Self reference tables

I have a class of companies and sub companies. These can be nested to any level and displayed in a treeview. I am trying to figure out how to do a self reference in ormlite to build out a hierarchy using the below DTO. With the below I get ambiguous column name errors. Is this approach a bad idea with Service stack?
public class Company : DTOServiceStackBase
{
[AutoIncrement]
[PrimaryKey]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
[References(typeof(Company))]
public int ParentId { get; set; }
}
The below DTO works fine in ORMLite. I would prefer the cleaner implementation above.
public class Company : DTOServiceStackBase
{
[AutoIncrement]
[PrimaryKey]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
[Reference] // Save in SubCompanies table
public List<SubCompany> SubCompanies { get; set; }
}
public class SubCompany : Company
{
[References(typeof(Company))]
public int ChildCompanyId { get; set; }
[References(typeof(Company))]
public int ParentCompanyId { get; set; }
}
EDIT Based on Mythz's Response
Here is my working code for anyone who wants to use it.
[Route("/Company/{Id}", "GET")]
public class GetCompaniesById : IReturn<GetCompaniesFlatTree>
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int? ParentId { get; set; }
}
[Route("/CompaniesFlatTree", "GET")]
public class GetCompaniesFlatTree : IReturn<GetCompaniesFlatTree>
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int? ParentId { get; set; }
}
[Route("/CompaniesTree", "GET")]
public class GetCompaniesTree : IReturn<Company>{}
public class DTOServiceStackBase {
public ResponseStatus ResponseStatus { get; set; } //Automatic exception handling
}
public class Company : DTOServiceStackBase
{
[AutoIncrement]
[PrimaryKey]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
public int? ParentId { get; set; }
[IgnoreDataMember]
public List<Company> SubCompanies { get; set; }
}
[Authenticate]
public class CompanyService : Service
{
/// <summary>
/// Calling SQL directly and casting to the GetCompaniesFlatTree object
/// Don't do this methond of direct SQL unless you cannot do it any other way
/// Why?? Becuase the SQL is not automatically updated when we updated the database schema
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public object Get(GetCompaniesFlatTree request)
{
//This retun uses the DB.Select and works correctly
//return Db.Select<GetCompaniesFlatTree>($"SELECT SC.* FROM Company C Join Company SC ON SC.ParentId = C.Id Where C.ID = {request.Id}");
//This query uses Db.Query due to the BEGIN and CTE Usage
//This does not work with SQL in Memory because it does not support CTE Statements
return Db.Query<GetCompaniesFlatTree>("BEGIN WITH q AS ( SELECT * FROM [Company] WHERE ParentId IS NULL UNION ALL SELECT m.* FROM [Company] m JOIN q ON m.parentId = q.Id) SELECT * FROM q END;");
}
/// <summary>
/// Table Alias is required in this Select due to the self join on company.
/// Table Alisa allows the join to specify which table to return the data from.
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public object Get(GetCompaniesById request)
{
var q = Db.From<Company>(Db.TableAlias("c1"))
.Join<Company>((ChildComp, ParentCompany) =>
ChildComp.Id == ParentCompany.ParentId
&& ParentCompany.Id == request.Id, Db.TableAlias("c2")).Select<Company>(p => new {Id = Sql.TableAlias(p.Id, "c2"), Name = Sql.TableAlias(p.Name, "c2")});
var results = Db.Select<GetCompaniesById>(q);
//See the SQL that was generated
var lastSql = Db.GetLastSql();
return results;
}
/// <summary>
/// Get all Compaines and build the hierarchy
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public object Get(GetCompaniesTree request)
{
//Get all companies
var results = Db.Select<Company>();
//Get Top node
Company topCompany = results.Single(x => x.ParentId == null);
//Find all children
var allChildrenRecursive = GetAllChildrenRecursive(topCompany, results);
return allChildrenRecursive;
}
/// <summary>
/// Populates a Companies collection of child companies
/// </summary>
/// <param name="parent"></param>
/// <param name="results"></param>
/// <returns></returns>
private Company GetAllChildrenRecursive(Company parent, List<Company> results)
{
List<Company> retVal = new List<Company>();
retVal.Add(parent);
//Get Children
var children = results.Where(x => x.ParentId == parent.Id).ToList();
parent.SubCompanies = children;
foreach (var child in children)
{
GetAllChildrenRecursive(child, results);
}
return parent;
}
}
To maintain a tree relationship you would just need to have a nullable int? ParentId in the Company table where Company with NULL ParentId is the root company whilst iterating through the rest of the companies to populate a Dictionary<int,List<Company>> indexed by the parent Id.
This isn't related to an OrmLite Self Reference which just means to maintain the FK Reference to a different table on the table containing the reference.

WebAPI calls a custom validation attribute twice on POST method call - normal?

My issue:
A custom validation attribute is called twice and not once when calling a webapi post method (with EF) - I am not sure if this is normal and would like a definitive answer. It validates at the following points:
Just before the breakpoint enters the webapi application post method (presumably populating ModelState)
Again just before the insert takes place (db.Applications.Add(application))
[Table("Applications")]
public class Application
{
/// <summary>
/// ApplicationID (auto-increment)
/// </summary>
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ApplicationID { get; set; }
/// <summary>
/// Name of the application
/// </summary>
[Required]
[MaxLength(255)]
public string ApplicationName { get; set; }
/// <summary>
/// Application ref (for friendly lookups)
/// </summary>
[Required]
[MaxLength(150)]
[UniqueApplicationReference] // <<<<<<< My custom validation attribute
public string ApplicationRef { get; set; }
/// <summary>
/// Application status
/// </summary>
[Required]
public bool? ApplicationStatus { get; set; }
public virtual ICollection<ApplicationFeature> ApplicationFeatures { get; set; }
}
Here is my webAPI end point:
public HttpResponseMessage PostApplication(Application application)
{
if (ModelState.IsValid)
{
db.Applications.Add(application);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, application);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = application.ApplicationID }));
return response;
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
Is the better solution simply to provide a Data Transfer class for application object with little/simple validation on it for the purposes of passing the data and then let any domain specific validation errors just bubble back via HttpResponseMessage therefore lookups are only run when the insert is attempted with reasonable data?
Thank you!
Dan.

Entity How to allow ForeignKey in parent child relationship?

I have Entity
/// <summary>
/// The greoup.
/// </summary>
public class Group
{
#region Public Properties
/// <summary>
/// Gets or sets the group id.
/// </summary>
[Key]
public int GroupId { get; set; }
/// <summary>
/// Gets or sets the parent group id.
/// </summary>
public int ParentGroupId { get; set; }
/// <summary>
/// Gets or sets the active.
/// </summary>
public int Active { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
public string Description { get; set; }
/// <summary>
/// Gets or sets the group guid.
/// </summary>
public Guid GroupGuid { get; set; }
/// <summary>
/// Gets or sets the order weight.
/// </summary>
public int OrderWeight { get; set; }
/// <summary>
/// Gets or sets the parent group.
/// </summary>
[ForeignKey("ParentGroupId")]
public virtual Group ParentGroup { get; set; }
/// <summary>
/// Gets or sets the groups.
/// </summary>
public virtual ICollection<Group> Groups { get; set; }
#endregion
}
How I can to allow exited foreign key. Because when I try add Group when ParentGroupId = 0. I get exception
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
you are creating a self referencing table in the database with this structure. So the ParentGroupId must be nullable. The root node cannot have a parent , all the nodes arising from it will however have a parent, so you need to make the ParentGroupId nullable.

The entity types 'AdminServiceCenter' and 'ManagerServiceCenter' cannot share table 'SrvCenters'

I was created two class.
[Table("SrvCenters")]
public class ManagerServiceCenter
{
#region Public Properties
/// <summary>
/// Gets or sets the srv center id.
/// </summary>
[Key]
public int SrvCenterId { get; set; }
/// <summary>
/// Gets or sets the branch id.
/// </summary>
public int BranchId { get; set; }
/// <summary>
/// Gets or sets the branch.
/// </summary>
public virtual Branch Branch { get; set; }
/// <summary>
/// Gets or sets the location id.
/// </summary>
public int LocationId { get; set; }
/// <summary>
/// Gets or sets the location.
/// </summary>
public virtual Location Location { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
public string Description { get; set; }
/// <summary>
/// Gets or sets the domain name.
/// </summary>
public string DomainName { get; set; }
/// <summary>
/// Gets or sets the active.
/// </summary>
public int Active { get; set; }
#endregion
}
and
[Table("SrvCenters")]
public class AdminServiceCenter
{
#region Public Properties
/// <summary>
/// Gets or sets the srv center id.
/// </summary>
[Key]
public int SrvCenterId { get; set; }
/// <summary>
/// Gets or sets the active.
/// </summary>
public int Active { get; set; }
/// <summary>
/// Gets or sets the begin break.
/// </summary>
public TimeSpan BeginBreak { get; set; }
/// <summary>
/// Gets or sets the begin day.
/// </summary>
public TimeSpan BeginDay { get; set; }
/// <summary>
/// Gets or sets the branch id.
/// </summary>
public int BranchId { get; set; }
/// <summary>
/// Gets or sets the branch.
/// </summary>
public virtual Branch Branch { get; set; }
/// <summary>
/// Gets or sets a value indicating whether check time table.
/// </summary>
public int CheckTimeTable { get; set; }
/// <summary>
/// Gets or sets the create time.
/// </summary>
public DateTime? CreateTime { get; set; }
/// <summary>
/// Gets or sets the create user.
/// </summary>
public string CreateUser { get; set; }
/// <summary>
/// Gets or sets the cust priority id.
/// </summary>
public int CustPriorityId { get; set; }
/// <summary>
/// Gets or sets the customer bind id.
/// </summary>
public int CustomerBindId { get; set; }
/// <summary>
/// Gets or sets the delete count.
/// </summary>
public int DeleteCount { get; set; }
/// <summary>
/// Gets or sets the delete time.
/// </summary>
public int DeleteTime { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
public string Description { get; set; }
/// <summary>
/// Gets or sets the domain name.
/// </summary>
public string DomainName { get; set; }
/// <summary>
/// Gets or sets the end break.
/// </summary>
public TimeSpan EndBreak { get; set; }
/// <summary>
/// Gets or sets the end day.
/// </summary>
public TimeSpan EndDay { get; set; }
/// <summary>
/// Gets or sets a value indicating whether estimate wait time.
/// </summary>
public int EstimateWaitTime { get; set; }
/// <summary>
/// Gets or sets a value indicating whether have break.
/// </summary>
public int HaveBreak { get; set; }
/// <summary>
/// Gets or sets the hold order.
/// </summary>
public int HoldOrder { get; set; }
/// <summary>
/// Gets or sets the location id.
/// </summary>
public int LocationId { get; set; }
/// <summary>
/// Gets or sets the notif interval.
/// </summary>
public int NotifInterval { get; set; }
/// <summary>
/// Gets or sets a value indicating whether notification.
/// </summary>
public int Notification { get; set; }
/// <summary>
/// Gets or sets the priority direct id.
/// </summary>
public int PriorityDirectId { get; set; }
/// <summary>
/// Gets or sets the serv priority id.
/// </summary>
public int ServPriorityId { get; set; }
/// <summary>
/// Gets or sets the service bind id.
/// </summary>
public int ServiceBindId { get; set; }
/// <summary>
/// Gets or sets the srv center guid.
/// </summary>
public Guid SrvCenterGuid { get; set; }
/// <summary>
/// Gets or sets the sequence id.
/// </summary>
public int SequenceId { get; set; }
/// <summary>
/// Gets or sets the sequence.
/// </summary>
public virtual Sequence Sequence { get; set; }
#endregion
}
When try I called ManagerServiceCenter. I got exception
The entity types 'AdminServiceCenter' and 'ManagerServiceCenter' cannot share table 'SrvCenters' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them.
Ok
Why this class working :)
/// <summary>
/// The workplace setting.
/// </summary>
[Table("SrvCenters")]
public class WorkplaceSetting
{
/// <summary>
/// Gets or sets the srv center id.
/// </summary>
[Key]
public int SrvCenterId { get; set; }
/// <summary>
/// Gets or sets the net ident type id.
/// </summary>
public int NetIdentTypeId { get; set; }
/// <summary>
/// Gets or sets the help net ident type.
/// </summary>
[ForeignKey("NetIdentTypeId")]
public virtual HelpNetIdentType HelpNetIdentType { get; set; }
/// <summary>
/// Gets or sets the w p_ auto start exec.
/// </summary>
public int WP_AutoStartExec { get; set; }
/// <summary>
/// Gets or sets the w p_ button mask.
/// </summary>
public int WP_ButtonMask { get; set; }
/// <summary>
/// Gets or sets the w p_ show info.
/// </summary>
public int WP_ShowInfo { get; set; }
/// <summary>
/// Gets or sets the w p_ show message.
/// </summary>
public int WP_ShowMessage { get; set; }
/// <summary>
/// Gets or sets the w p_ time between msg.
/// </summary>
public int WP_TimeBetweenMsg { get; set; }
/// <summary>
/// Gets or sets the w p_ time in idle.
/// </summary>
public int WP_TimeInIdle { get; set; }
/// <summary>
/// Gets or sets the w p_ time in resolve.
/// </summary>
public int WP_TimeInResolve { get; set; }
/// <summary>
/// Gets or sets the w p_ time show msg.
/// </summary>
public int WP_TimeShowMsg { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
public string Description { get; set; }
/// <summary>
/// Gets or sets the active.
/// </summary>
public int Active { get; set; }
}
You must use TPH inheritance or table splitting if you want to share table between two entities.

NopCommerce webservice DTO's

I am trying to extend webservice plygin in order to get from nopcommerce the List of Products.
What I have done is the following. I created a class like ProductDto, and inside NopService.cs I created a method like:
public List<ProductDto> GetProductCollection(string usernameOrEmail, string userPassword)
ProductDto is taken from Product using AutoMapper. (shown below)
BUT IT DOES NOT WORK :( what am I missing?
Any ideas? The idea behind all this is to connect nopcommerce with my ERP through webservices
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Nop.Core.Domain.Catalog
{
public class ProductDto
{
public virtual string Name { get; set; }
// Instance members must be virtual on data table objects like Affiliate.cs
// Virtual is required by data access frameworks so that these frameworks
// can implement more complex features like lazy loading.
public virtual string ProductGID { get; set; }
/// <summary>
/// Gets or sets the short description
/// </summary>
public virtual string ShortDescription { get; set; }
/// <summary>
/// Gets or sets the full description
/// </summary>
public virtual string FullDescription { get; set; }
/// <summary>
/// Gets or sets the admin comment
/// </summary>
public virtual string AdminComment { get; set; }
/// <summary>
/// Gets or sets a value of used product template identifier
/// </summary>
public virtual int ProductTemplateId { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to show the product on home page
/// </summary>
public virtual bool ShowOnHomePage { get; set; }
/// <summary>
/// Gets or sets the meta keywords
/// </summary>
public virtual string MetaKeywords { get; set; }
/// <summary>
/// Gets or sets the meta description
/// </summary>
public virtual string MetaDescription { get; set; }
/// <summary>
/// Gets or sets the meta title
/// </summary>
public virtual string MetaTitle { get; set; }
/// <summary>
/// Gets or sets the search-engine name
/// </summary>
public virtual string SeName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the product allows customer reviews
/// </summary>
public virtual bool AllowCustomerReviews { get; set; }
/// <summary>
/// Gets or sets the rating sum (approved reviews)
/// </summary>
public virtual int ApprovedRatingSum { get; set; }
/// <summary>
/// Gets or sets the rating sum (not approved reviews)
/// </summary>
public virtual int NotApprovedRatingSum { get; set; }
/// <summary>
/// Gets or sets the total rating votes (approved reviews)
/// </summary>
public virtual int ApprovedTotalReviews { get; set; }
/// <summary>
/// Gets or sets the total rating votes (not approved reviews)
/// </summary>
public virtual int NotApprovedTotalReviews { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the entity is published
/// </summary>
public virtual bool Published { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the entity has been deleted
/// </summary>
public virtual bool Deleted { get; set; }
/// <summary>
/// Gets or sets the date and time of product creation
/// </summary>
public virtual DateTime CreatedOnUtc { get; set; }
/// <summary>
/// Gets or sets the date and time of product update
/// </summary>
public virtual DateTime UpdatedOnUtc { get; set; }
}
}
And inside NopService.cs I created a method like this
public List<ProductDto> GetProductCollection(string usernameOrEmail, string userPassword)
{
CheckAccess(usernameOrEmail, userPassword);
if (!_permissionSettings.Authorize(StandardPermissionProvider.ManageCatalog))
throw new ApplicationException("Not allowed to manage Catalog");
var productslist = new List<Product>();
productslist.AddRange(_productService.GetAllProducts(false));
List<Product> products = productslist;
List<ProductDto> productsDtos = Mapper.Map<List<Product>, List<ProductDto>>(products);
return productsDtos;
}
}
Your mapping should only be configured once, during startup. So try moving it to a profile:
namespace MyNamespace
{
using System.Collections.Generic;
using AutoMapper;
public class ProductProfile : Profile
{
public override string ProfileName
{
get
{
return "ProductProfile";
}
}
protected override void Configure()
{
Mapper.CreateMap<Product, ProductDto>();
Mapper.CreateMap<ProductCategory, ProductCategoryDto>();
// etc
}
}
}
Then initialise it during start up (Application_Start, etc):
Mapper.Initialize(m => m.AddProfile<ProductProfile>());
Create a unit test to check your mappings:
namespace MyNamespace
{
using System.Collections.Generic;
using AutoMapper;
using NUnit.Framework;
[TestFixture]
public class AutoMapperTests
{
[Test]
public void AutoMapper_Configuration_IsValid()
{
Mapper.Initialize(m => m.AddProfile<ProductProfile>());
Mapper.AssertConfigurationIsValid();
}
}
}
This will help determine if there are any errors in your mapping configuration, and show you what to fix. Once this is done, you can then focus on getting the functionality working.
I managed to make it work but with problems in mapping nested classes.
Product class
public partial class Product : BaseEntity, ILocalizedEntity
{
private ICollection<ProductVariant> _productVariants;
private ICollection<ProductCategory> _productCategories;
private ICollection<ProductManufacturer> _productManufacturers;
private ICollection<ProductPicture> _productPictures;
private ICollection<ProductReview> _productReviews;
private ICollection<ProductSpecificationAttribute> _productSpecificationAttributes;
private ICollection<ProductTag> _productTags;
/// <summary>
/// Gets or sets the name
/// </summary>
public virtual string Name { get; set; }
// Instance members must be virtual on data table objects like Affiliate.cs
// Virtual is required by data access frameworks so that these frameworks
// can implement more complex features like lazy loading.
public virtual string ProductGID { get; set; }
/// <summary>
/// Gets or sets the short description
/// </summary>
public virtual string ShortDescription { get; set; }
/// <summary>
/// Gets or sets the full description
/// </summary>
public virtual string FullDescription { get; set; }
/// <summary>
/// Gets or sets the admin comment
/// </summary>
public virtual string AdminComment { get; set; }
/// <summary>
/// Gets or sets a value of used product template identifier
/// </summary>
public virtual int ProductTemplateId { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to show the product on home page
/// </summary>
public virtual bool ShowOnHomePage { get; set; }
/// <summary>
/// Gets or sets the meta keywords
/// </summary>
public virtual string MetaKeywords { get; set; }
/// <summary>
/// Gets or sets the meta description
/// </summary>
public virtual string MetaDescription { get; set; }
/// <summary>
/// Gets or sets the meta title
/// </summary>
public virtual string MetaTitle { get; set; }
/// <summary>
/// Gets or sets the search-engine name
/// </summary>
public virtual string SeName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the product allows customer reviews
/// </summary>
public virtual bool AllowCustomerReviews { get; set; }
/// <summary>
/// Gets or sets the rating sum (approved reviews)
/// </summary>
public virtual int ApprovedRatingSum { get; set; }
/// <summary>
/// Gets or sets the rating sum (not approved reviews)
/// </summary>
public virtual int NotApprovedRatingSum { get; set; }
/// <summary>
/// Gets or sets the total rating votes (approved reviews)
/// </summary>
public virtual int ApprovedTotalReviews { get; set; }
/// <summary>
/// Gets or sets the total rating votes (not approved reviews)
/// </summary>
public virtual int NotApprovedTotalReviews { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the entity is published
/// </summary>
public virtual bool Published { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the entity has been deleted
/// </summary>
public virtual bool Deleted { get; set; }
/// <summary>
/// Gets or sets the date and time of product creation
/// </summary>
public virtual DateTime CreatedOnUtc { get; set; }
/// <summary>
/// Gets or sets the date and time of product update
/// </summary>
public virtual DateTime UpdatedOnUtc { get; set; }
/// <summary>
/// Gets or sets the product variants
/// </summary>
public virtual ICollection<ProductVariant> ProductVariants
{
get { return _productVariants ?? (_productVariants = new List<ProductVariant>()); }
protected set { _productVariants = value; }
}
/// <summary>
/// Gets or sets the collection of ProductCategory
/// </summary>
public virtual ICollection<ProductCategory> ProductCategories
{
get { return _productCategories ?? (_productCategories = new List<ProductCategory>()); }
protected set { _productCategories = value; }
}
/// <summary>
/// Gets or sets the collection of ProductManufacturer
/// </summary>
public virtual ICollection<ProductManufacturer> ProductManufacturers
{
get { return _productManufacturers ?? (_productManufacturers = new List<ProductManufacturer>()); }
protected set { _productManufacturers = value; }
}
/// <summary>
/// Gets or sets the collection of ProductPicture
/// </summary>
public virtual ICollection<ProductPicture> ProductPictures
{
get { return _productPictures ?? (_productPictures = new List<ProductPicture>()); }
protected set { _productPictures = value; }
}
/// <summary>
/// Gets or sets the collection of product reviews
/// </summary>
public virtual ICollection<ProductReview> ProductReviews
{
get { return _productReviews ?? (_productReviews = new List<ProductReview>()); }
protected set { _productReviews = value; }
}
/// <summary>
/// Gets or sets the product specification attribute
/// </summary>
public virtual ICollection<ProductSpecificationAttribute> ProductSpecificationAttributes
{
get { return _productSpecificationAttributes ?? (_productSpecificationAttributes = new List<ProductSpecificationAttribute>()); }
protected set { _productSpecificationAttributes = value; }
}
/// <summary>
/// Gets or sets the product specification attribute
/// </summary>
public virtual ICollection<ProductTag> ProductTags
{
get { return _productTags ?? (_productTags = new List<ProductTag>()); }
protected set { _productTags = value; }
}
}
}
ProductDto Class
public class ProductDto
{
private ICollection<ProductCategoryDto> _productCategories;
public virtual string Name { get; set; }
// Instance members must be virtual on data table objects like Affiliate.cs
// Virtual is required by data access frameworks so that these frameworks
// can implement more complex features like lazy loading.
public virtual string ProductGID { get; set; }
/// <summary>
/// Gets or sets the short description
/// </summary>
public virtual string ShortDescription { get; set; }
/// <summary>
/// Gets or sets the full description
/// </summary>
public virtual string FullDescription { get; set; }
/// <summary>
/// Gets or sets the admin comment
/// </summary>
public virtual string AdminComment { get; set; }
/// <summary>
/// Gets or sets a value of used product template identifier
/// </summary>
public virtual int ProductTemplateId { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to show the product on home page
/// </summary>
public virtual bool ShowOnHomePage { get; set; }
/// <summary>
/// Gets or sets the meta keywords
/// </summary>
public virtual string MetaKeywords { get; set; }
/// <summary>
/// Gets or sets the meta description
/// </summary>
public virtual string MetaDescription { get; set; }
/// <summary>
/// Gets or sets the meta title
/// </summary>
public virtual string MetaTitle { get; set; }
/// <summary>
/// Gets or sets the search-engine name
/// </summary>
public virtual string SeName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the product allows customer reviews
/// </summary>
public virtual bool AllowCustomerReviews { get; set; }
/// <summary>
/// Gets or sets the rating sum (approved reviews)
/// </summary>
public virtual int ApprovedRatingSum { get; set; }
/// <summary>
/// Gets or sets the rating sum (not approved reviews)
/// </summary>
public virtual int NotApprovedRatingSum { get; set; }
/// <summary>
/// Gets or sets the total rating votes (approved reviews)
/// </summary>
public virtual int ApprovedTotalReviews { get; set; }
/// <summary>
/// Gets or sets the total rating votes (not approved reviews)
/// </summary>
public virtual int NotApprovedTotalReviews { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the entity is published
/// </summary>
public virtual bool Published { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the entity has been deleted
/// </summary>
public virtual bool Deleted { get; set; }
/// <summary>
/// Gets or sets the date and time of product creation
/// </summary>
public virtual DateTime CreatedOnUtc { get; set; }
/// <summary>
/// Gets or sets the date and time of product update
/// </summary>
public virtual DateTime UpdatedOnUtc { get; set; }
/// <summary>
/// Gets or sets the collection of ProductCategory
/// </summary>
public virtual ICollection<ProductCategoryDto> ProductCategories
{
get { return _productCategories ?? (_productCategories = new List<ProductCategoryDto>()); }
protected set { _productCategories = value; }
}
}
}
ProductCategory Class
public partial class ProductCategory : BaseEntity
{
public virtual int ProductId { get; set; }
/// <summary>
/// Gets or sets the category identifier
/// </summary>
public virtual int CategoryId { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the product is featured
/// </summary>
public virtual bool IsFeaturedProduct { get; set; }
/// <summary>
/// Gets or sets the display order
/// </summary>
public virtual int DisplayOrder { get; set; }
/// <summary>
/// Gets the category
/// </summary>
public virtual Category Category { get; set; }
/// <summary>
/// Gets the product
/// </summary>
public virtual Product Product { get; set; }
}
And ProductCategoryDto Class
public class ProductCategoryDto
{
/// <summary>
/// Gets or sets the product identifier
/// </summary>
public virtual int ProductId { get; set; }
/// <summary>
/// Gets or sets the category identifier
/// </summary>
public virtual int CategoryId { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the product is featured
/// </summary>
public virtual bool IsFeaturedProduct { get; set; }
/// <summary>
/// Gets or sets the display order
/// </summary>
public virtual int DisplayOrder { get; set; }
/// <summary>
/// Gets the category
/// </summary>
public virtual CategoryDto Category { get; set; }
/// <summary>
/// Gets the product
/// </summary>
public virtual ProductDto Product { get; set; }
}
Mapping is done inside a method like this
public List<ProductDto> GetProductCollection(string usernameOrEmail, string userPassword)
{
CheckAccess(usernameOrEmail, userPassword);
if (!_permissionSettings.Authorize(StandardPermissionProvider.ManageCatalog))
throw new ApplicationException("Not allowed to manage Catalog");
List<Product> productslist = new List<Product>();
productslist.AddRange(_productService.GetAllProducts(false));
List<ProductCategory> productCategorylist = new List<ProductCategory>();
foreach (var product in productslist)
{
productCategorylist.AddRange(_categoryService.GetProductCategoriesByProductId(product.Id, false));
}
Mapper.CreateMap<Product, ProductDto>();
Mapper.CreateMap<ProductCategory, ProductCategoryDto>();
List<ProductDto> productsDto = Mapper.Map<List<Product>, List<ProductDto>>(productslist);
List<ProductCategoryDto> productCategoriesDto = Mapper.Map<List<ProductCategory>, List<ProductCategoryDto>>(productCategorylist);
return productsDto;
}
The problem is that I never get the mapping from ProductDto to the Icollection Of ProductCategoriesDto. Everything else works fine.

Resources