Extending Nop data model

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
12 years ago
Hello guys

i am trying to write a plugIn for the V 2.3

Actualy i am having a problem , I have a datamodel in my plugIn that have a manyRelation with the product ,
i added two datamodels:

public class Fake
{
   public virtual ICollection<FakeProduct > fakeProducts {get;set;}
}

public class FakeProduct
{
   public virtual int id {get;set;}
   public virtual Fake fake {get;set;}
   public virtual Product product {get;set;}

   public virtual int productId { get; set; }
   public virtual int fakeId { get; set; }
}

but during the objectContext istall of my plugIn I have an error that says :

Unable to determine the principal end of an association between the types 'Nop.Core.Domain.Customers.RewardPointsHistory' and 'Nop.Core.Domain.Orders.Order'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations


I don't want to modify the nopCommerce solution.

please help me


Marco
12 years ago
Have you defined classes derived from EntityTypeConfiguration<Fake> and EntityTypeConfiguration<FakeProduct>?

I recommend you looking at the following article: https://www.nopcommerce.com/docs/75/plugin-with-data-access.aspx
12 years ago
yes I did, this is my class

public class FakeProcutMap : EntityTypeConfiguration<FakeProduct>
    {
        public FakeProcutMap()
        {
            this.ToTable("Fake_Product_Mapping");
            this.HasKey(pc => pc.Id);

            this.HasRequired(pc => pc.fake)
                .WithMany(c => c.fakeProducts)
                .HasForeignKey(pc => pc.fakeId);

            //this.HasRequired(pc => pc.product)
            //    .WithMany()
            //    .HasForeignKey(pc => pc.productId);
          
        }
    }


I think it is correct my mapping

Hope you can help me

Marco
12 years ago
doriana.mirabello wrote:
Unable to determine the principal end of an association between the types 'Nop.Core.Domain.Customers.RewardPointsHistory' and 'Nop.Core.Domain.Orders.Order'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations

I've just noticed that your error is about absolutely other entities ('RewardPointsHistory' and 'Order'). Do you reference them in your plugin?
12 years ago
No, I don't have any  reference to 'RewardPointsHistory' and 'Order', for that I cant' really understand what's is going on,

i post to my object context class, so you have the complete vision:



    public class FakeObjectContext : DbContext, IDbContext
    {
        public FakeObjectContext(string nameOrConnectionString)
            : base(nameOrConnectionString)
        {
            
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new FakeMap());
            modelBuilder.Configurations.Add(new FakeProcutMap());


            //disable EdmMetadata generation
            modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
            base.OnModelCreating(modelBuilder);
        }

        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 all tables
            //TODO don't create the table if it already exists
            var dbScript = CreateDatabaseScript();
            Database.ExecuteSqlCommand(dbScript);
            SaveChanges();
        }

        /// <summary>
        /// Uninstall
        /// </summary>
        public void Uninstall()
        {
            //Database.ExecuteSqlCommand(dbScript);
            SaveChanges();
        }

        public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
        {
            throw new NotImplementedException();
        }

    }
12 years ago
I have exactly the same problem in 2.3 version plugin

This is my class:

--------
public class Action : BaseEntity
    {
        public virtual string Title { get; set; }
        public virtual string Description { get; set; }
        public virtual DateTime StartDate { get; set; }
        public virtual DateTime FinishDate { get; set; }
        public virtual decimal? Amount { get; set; }

        public virtual ICollection<ActionTargetProductVariant> TargetProducts { get; set; }
    }
------------

When I remove TargetProducts property, everything works fine. But with this property I have the same message when I'm trying to get entities (actionRepository.Table.ToList()):

-------------
"Unable to determine the principal end of an association between the types 'Nop.Core.Domain.Customers.RewardPointsHistory' and 'Nop.Core.Domain.Orders.Order'. The principal end of this association must be explicitly configured using either the relationship...
-------------

Looks like something really wrong with RewardPointsHistory and Order.

Thanks in advance for any ideas how to fix this problem.

Regards,
Serge.
12 years ago
Serge Gusev wrote:
I have exactly the same problem in 2.3 version plugin

Could you share your plugin source code?
12 years ago
a.m. wrote:
Could you share your plugin source code?


My plugin’s code:

--------------------------------------
Domain:
---------

    public class Action : BaseEntity
    {
        public virtual string Title { get; set; }
        public virtual string Description { get; set; }
        public virtual DateTime StartDate { get; set; }
        public virtual DateTime FinishDate { get; set; }
        public virtual decimal? Amount { get; set; }

        public virtual ICollection<ActionTargetProductVariant> TargetProducts { get; set; }
    }

    public class ActionTargetProductVariant: BaseEntity
    {
        public virtual int ActionId { get; set; }
        public virtual int ProductVariantId { get; set; }
        public virtual Action Action { get; set; }
        public virtual ProductVariant ProductVariant { get; set; }
        public virtual decimal? Price { get; set; }
    }

Data:
------
    public partial class ActionMap : EntityTypeConfiguration<Nop.Plugin.SoftAIN.Action.Domain.Action>
    {
        public ActionMap()
        {
            this.ToTable("Action");
            this.HasKey(a => a.Id);

            this.Property(a => a.Title).IsRequired().HasMaxLength(256);
            this.Property(a => a.Description).IsRequired();
            this.Property(a => a.StartDate).IsRequired();
            this.Property(a => a.FinishDate).IsRequired();
            this.Property(a => a.Amount);
        }
    }

   public partial class ActionTargetProductVariantMap :   EntityTypeConfiguration<Nop.Plugin.SoftAIN.Action.Domain.ActionTargetProductVariant>
    {
        public ActionTargetProductVariantMap()
        {
            this.ToTable("Action_TargetProductVariant");
            this.HasKey(atp => atp.Id);

            this.Property(atp => atp.ActionId);
            this.Property(atp => atp.ProductVariantId);

            this.HasRequired(atp => atp.Action)
                .WithMany(a => a.TargetProducts)
                .HasForeignKey(atp => atp.ActionId);

            this.HasRequired(atp => atp.ProductVariant);
        }

    public class ActionObjectContext : DbContext, IDbContext
    {
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new ActionMap());
            modelBuilder.Configurations.Add(new ActionTargetProductVariantMap());
        
            modelBuilder.Conventions.Remove<IncludeMetadataConvention>();

            base.OnModelCreating(modelBuilder);
        }


Registration:
--------------
    public class DependencyRegistrar : IDependencyRegistrar
    {
        private const string CONTEXT_NAME = "nop_object_context_softain_action";

        public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
        {
            //Load custom data settings
            var dataSettingsManager = new DataSettingsManager();
            DataSettings dataSettings = dataSettingsManager.LoadSettings();

            //Register custom object context
            builder.Register<IDbContext>(c => RegisterIDbContext(c, dataSettings)).Named<IDbContext>(CONTEXT_NAME).InstancePerHttpRequest();
            builder.Register(c => RegisterIDbContext(c, dataSettings)).InstancePerHttpRequest();

            
            builder.RegisterType<EfRepository<SoftAIN.Action.Domain.Action>>().As<IRepository<SoftAIN.Action.Domain.Action>>().WithParameter(ResolvedParameter.ForNamed<IDbContext>(CONTEXT_NAME)).InstancePerHttpRequest();
            builder.RegisterType<EfRepository<SoftAIN.Action.Domain.ActionTargetProductVariant>>().As<IRepository<SoftAIN.Action.Domain.ActionTargetProductVariant>>().WithParameter(ResolvedParameter.ForNamed<IDbContext>(CONTEXT_NAME)).InstancePerHttpRequest();
        }

-----------------------------------------------------

I don’t know why my own named DbContext depend on the common one, but it does.

Btw, these changes fix the problem:

----------------------------------------------------------------------------------------------------------

Order (collection instead one property):
---------------------------------------------
         public virtual ICollection<RewardPointsHistory> RedeemedRewardPointsEntities { get; set; }
        
        public RewardPointsHistory RedeemedRewardPointsEntry
        {
            get { return RedeemedRewardPointsEntities == null ? null : RedeemedRewardPointsEntities.FirstOrDefault(); }
            set { }
        }

RewardPointsHistory (added property)
-------------------------------------------

public virtual int UsedWithOrderId { get; set; }

RewardPointsHistoryMap (moving to WithMany):
------------------------------------------------------

         this.HasRequired(rph => rph.UsedWithOrder)
                .WithMany(o => o.RedeemedRewardPointsEntities )
                .HasForeignKey(rph => rph.UsedWithOrderId);

And, in the database, in table RewardPointsHistory I replaced UsedWithOrder_Id by UsedWithOrderId to follow the conditions (it prevernts creating a new FK by EF).

I understand, that the fixes above is a workaround, not the solution, so, maybe, there is a better way?
12 years ago
Serge Gusev wrote:
My plugin’s code...

Thanks. I'll try it out a bit later
12 years ago
Just played with the plugin (Serge Gusev's one) and mentioned that 'ActionTargetProductVariant' has a reference to 'ProductVariant' which is already a part of the core NopObjectContext. You can simply remove this property and use IProductService for product variant loading. Of course, it limits plugin extensibility, but it works. I'll also ask EF Code First team why it happens
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.