Winged-edge vs half-edge - cad

I try to understand boundary representation (B-rep) and I cannot find what's the advantages of the half-edge data structure versus the winged-edge one. I've found in this book that winged-edge cannot represent the state where a vertex but no edges exists in space. But there is no sample.
Another book says that there is an ambiguity in the edge direction.
And finally, on a this web page, performance reasons are invoked.

I've found the solution in this paper.
With winged-edge, you've got this data structure:
The code in C# is as follow:
public class WingedEdge
{
public Curve3d Curve { get; set; }
/// <summary>
/// Edge of the left loop starting on the end vertex of this edge.
/// </summary>
public Edge EndLeftEdge { get; set; }
/// <summary>
/// Edge of the right loop starting on the end vertex of this edge.
/// </summary>
public Edge EndRightEdge { get; set; }
/// <summary>
/// Vertex on the end point of the edge.
/// </summary>
public Vertex EndVertex { get; set; }
/// <summary>
/// Face on the left side of the edge.
/// </summary>
public Face LeftFace { get; set; }
/// <summary>
/// Face on the right side of the edge.
/// </summary>
public Face RightFace { get; set; }
/// <summary>
/// Edge of the left loop ending on the start vertex of this edge.
/// </summary>
public Edge StartLeftEdge { get; set; }
/// <summary>
/// Edge of the right loop ending on the start vertex of this edge.
/// </summary>
public Edge StartRightEdge { get; set; }
/// <summary>
/// Vertex on the start point of the edge.
/// </summary>
public Vertex StartVertex { get; set; }
}
On the face, you only need to store one of the bounding edge, as the structure forms a double linked list, you can retrieve the other edges:
public class Face
{
/// <summary>
/// One of the edges bounding this face.
/// </summary>
public WingedEdge FirstEdge { get; set; }
}
But if you need to iterate over the edges of a face, you will use this code:
WingedEdge edge = face.FirstEdge;
do {
// Do something with the edge
WingedEdge edge = edge.LeftFace == face ? edge.LeftNextEdge : edge.RightNextEdge;
} while (edge != face.FirstEdge)
We have to use a conditional expression (? :) in the loop to find the next edge. On modern processors, this lead to a performance hit, as described in this post.
The half-edge data structure has not this problem (but takes more memory).

Related

how to add multiple tables in single nopcommerce plugin

I want to create a plugin in nopcommerce which has many options for weight shipping and i need 2 tables,one for shipping details and another one for neighboring province which has many-to-many relation with province table.
how can i create two tables in one plugin?
all documentations explain single table in single plugin.
public partial class ShippingByWeightRecord : BaseEntity
{
/// <summary>
/// Gets or sets the store identifier
/// </summary>
public int StoreId { get; set; }
/// <summary>
/// Gets or sets the warehouse identifier
/// </summary>
public int WarehouseId { get; set; }
/// <summary>
/// Gets or sets the country identifier
/// </summary>
public int CountryId { get; set; }
/// <summary>
/// Gets or sets the state/province identifier
/// </summary>
public int StateProvinceId { get; set; }
/// <summary>
/// Gets or sets the zip
/// </summary>
public string Zip { get; set; }
/// <summary>
/// Gets or sets the shipping method identifier
/// </summary>
public int ShippingMethodId { get; set; }
/// <summary>
/// Gets or sets the "from" value
/// </summary>
public decimal From { get; set; }
/// <summary>
/// Gets or sets the "to" value
/// </summary>
public decimal To { get; set; }
/// <summary>
/// Gets or sets the additional fixed cost
/// </summary>
public decimal AdditionalFixedCost { get; set; }
/// <summary>
/// Gets or sets the shipping charge percentage (of subtotal)
/// </summary>
public decimal PercentageRateOfSubtotal { get; set; }
/// <summary>
/// Gets or sets the shipping charge amount (per weight unit)
/// </summary>
public decimal RatePerWeightUnit { get; set; }
/// <summary>
/// Gets or sets the lower weight limit
/// </summary>
public decimal LowerWeightLimit { get; set; }
}
public partial class NeighborProvinceRecord: BaseEntity
{
public int ProvinceId { get; set; }
public int NProvinceId { get; set; }
}
public partial class ShippingByWeightRecordMap : NopEntityTypeConfiguration<ShippingByWeightRecord>
{
public ShippingByWeightRecordMap()
{
this.ToTable("ShippingByWeight");
this.HasKey(x => x.Id);
this.Property(x => x.Zip).HasMaxLength(400);
}
}
public partial class NeighborProvinceMap: NopEntityTypeConfiguration
{
public NeighborProvinceMap()
{
this.ToTable("NeighborProvince");
this.HasKey(x => x.Id);
}
}
public class ShippingByWeightObjectContext : DbContext, IDbContext
{
#region Ctor
public ShippingByWeightObjectContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
//((IObjectContextAdapter) this).ObjectContext.ContextOptions.LazyLoadingEnabled = true;
}
#endregion
#region Utilities
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ShippingByWeightRecordMap());
//disable EdmMetadata generation
//modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
base.OnModelCreating(modelBuilder);
}
#endregion
#region Methods
public string CreateDatabaseScript()
{
return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
}
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
/// <summary>
/// Install
/// </summary>
public void Install()
{
//create the table
var dbScript = CreateDatabaseScript();
Database.ExecuteSqlCommand(dbScript);
SaveChanges();
}
/// <summary>
/// Uninstall
/// </summary>
public void Uninstall()
{
//drop the table
var tableName = this.GetTableName<ShippingByWeightRecord>();
//var tableName = "ShippingByWeight";
this.DropPluginTable(tableName);
}
/// <summary>
/// Execute stores procedure and load a list of entities at the end
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <param name="commandText">Command text</param>
/// <param name="parameters">Parameters</param>
/// <returns>Entities</returns>
public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
{
throw new NotImplementedException();
}
/// <summary>
/// Creates a raw SQL query that will return elements of the given generic type. The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type.
/// </summary>
/// <typeparam name="TElement">The type of object returned by the query.</typeparam>
/// <param name="sql">The SQL query string.</param>
/// <param name="parameters">The parameters to apply to the SQL query string.</param>
/// <returns>Result</returns>
public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
{
throw new NotImplementedException();
}
/// <summary>
/// Executes the given DDL/DML command against the database.
/// </summary>
/// <param name="sql">The command string</param>
/// <param name="doNotEnsureTransaction">false - the transaction creation is not ensured; true - the transaction creation is ensured.</param>
/// <param name="timeout">Timeout value, in seconds. A null value indicates that the default value of the underlying provider will be used</param>
/// <param name="parameters">The parameters to apply to the command string.</param>
/// <returns>The result returned by the database after executing the command.</returns>
public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters)
{
throw new NotImplementedException();
}
/// <summary>
/// Detach an entity
/// </summary>
/// <param name="entity">Entity</param>
public void Detach(object entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
((IObjectContextAdapter)this).ObjectContext.Detach(entity);
}
#endregion
#region Properties
/// <summary>
/// Gets or sets a value indicating whether proxy creation setting is enabled (used in EF)
/// </summary>
public virtual bool ProxyCreationEnabled
{
get
{
return this.Configuration.ProxyCreationEnabled;
}
set
{
this.Configuration.ProxyCreationEnabled = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether auto detect changes setting is enabled (used in EF)
/// </summary>
public virtual bool AutoDetectChangesEnabled
{
get
{
return this.Configuration.AutoDetectChangesEnabled;
}
set
{
this.Configuration.AutoDetectChangesEnabled = value;
}
}
#endregion
}
public class NeighborProvinceContext : DbContext, IDbContext
{
#region Ctor
public NeighborProvinceContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
//((IObjectContextAdapter) this).ObjectContext.ContextOptions.LazyLoadingEnabled = true;
}
#endregion
#region Utilities
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new NeighborProvinceMap());
//disable EdmMetadata generation
//modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
base.OnModelCreating(modelBuilder);
}
#endregion
#region Methods
public string CreateDatabaseScript()
{
return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
}
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
/// <summary>
/// Install
/// </summary>
public void Install()
{
//create the table
var dbScript = CreateDatabaseScript();
Database.ExecuteSqlCommand(dbScript);
SaveChanges();
}
/// <summary>
/// Uninstall
/// </summary>
public void Uninstall()
{
//drop the table
var tableName = this.GetTableName<NeighborProvinceRecord>();
//var tableName = "ShippingByWeight";
this.DropPluginTable(tableName);
}
/// <summary>
/// Execute stores procedure and load a list of entities at the end
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <param name="commandText">Command text</param>
/// <param name="parameters">Parameters</param>
/// <returns>Entities</returns>
public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
{
throw new NotImplementedException();
}
/// <summary>
/// Creates a raw SQL query that will return elements of the given generic type. The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type.
/// </summary>
/// <typeparam name="TElement">The type of object returned by the query.</typeparam>
/// <param name="sql">The SQL query string.</param>
/// <param name="parameters">The parameters to apply to the SQL query string.</param>
/// <returns>Result</returns>
public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
{
throw new NotImplementedException();
}
/// <summary>
/// Executes the given DDL/DML command against the database.
/// </summary>
/// <param name="sql">The command string</param>
/// <param name="doNotEnsureTransaction">false - the transaction creation is not ensured; true - the transaction creation is ensured.</param>
/// <param name="timeout">Timeout value, in seconds. A null value indicates that the default value of the underlying provider will be used</param>
/// <param name="parameters">The parameters to apply to the command string.</param>
/// <returns>The result returned by the database after executing the command.</returns>
public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters)
{
throw new NotImplementedException();
}
/// <summary>
/// Detach an entity
/// </summary>
/// <param name="entity">Entity</param>
public void Detach(object entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
((IObjectContextAdapter)this).ObjectContext.Detach(entity);
}
#endregion
#region Properties
/// <summary>
/// Gets or sets a value indicating whether proxy creation setting is enabled (used in EF)
/// </summary>
public virtual bool ProxyCreationEnabled
{
get
{
return this.Configuration.ProxyCreationEnabled;
}
set
{
this.Configuration.ProxyCreationEnabled = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether auto detect changes setting is enabled (used in EF)
/// </summary>
public virtual bool AutoDetectChangesEnabled
{
get
{
return this.Configuration.AutoDetectChangesEnabled;
}
set
{
this.Configuration.AutoDetectChangesEnabled = value;
}
}
#endregion
}
public class EfStartUpTask : IStartupTask
{
public void Execute()
{
//It's required to set initializer to null (for SQL Server Compact).
//otherwise, you'll get something like "The model backing the 'your context name' context has changed since the database was created. Consider using Code First Migrations to update the database"
Database.SetInitializer<ShippingByWeightObjectContext>(null);
Database.SetInitializer<NeighborProvinceContext>(null);
}
public int Order
{
//ensure that this task is run first
get { return 0; }
}
}
public class DependencyRegistrar : IDependencyRegistrar
{
/// <summary>
/// Register services and interfaces
/// </summary>
/// <param name="builder">Container builder</param>
/// <param name="typeFinder">Type finder</param>
/// <param name="config">Config</param>
public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)
{
builder.RegisterType<ShippingByWeightService>().As<IShippingByWeightService>().InstancePerLifetimeScope();
//data context
this.RegisterPluginDataContext<ShippingByWeightObjectContext>(builder, "nop_object_context_shipping_weight_zip");
this.RegisterPluginDataContext<NeighborProvinceContext>(builder, "nop_object_context_shipping_NeighborProvince");
//override required repository with our custom context
builder.RegisterType<EfRepository<ShippingByWeightRecord>>()
.As<IRepository<ShippingByWeightRecord>>()
.WithParameter(ResolvedParameter.ForNamed<IDbContext>("nop_object_context_shipping_weight_zip"))
.InstancePerLifetimeScope();
builder.RegisterType<EfRepository<NeighborProvinceRecord>>()
.As<IRepository<NeighborProvinceRecord>>()
.WithParameter(ResolvedParameter.ForNamed<IDbContext>("nop_object_context_shipping_NeighborProvince"))
.InstancePerLifetimeScope();
}
/// <summary>
/// Order of this dependency registrar implementation
/// </summary>
public int Order
{
get { return 1; }
}
}

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