Question

I'm stuck on the tricky case, I've faced when integrated ASP.NET dynamic data + EF6

I have few tables: Products -> ProductSales, mapping is 1 - MANY

Database has been created using EF CF approach.

Some sort of code from Init schema.

 CreateTable(
                    "dbo.ProductSales",
                    c => new
                        {
                            Id = c.Long(nullable: false, identity: true),
                            IsActive = c.Boolean(nullable: false),
                            Price = c.Int(nullable: false),
                            Description = c.String(),
                            Name = c.String(nullable: false),
                            Product_Id = c.Long(),
                        })
                    .PrimaryKey(t => t.Id)
                    .ForeignKey("dbo.Products", t => t.Product_Id)
                    .Index(t => t.Product_Id);
CreateTable(
                "dbo.Products",
                c => new
                    {
                        Id = c.Long(nullable: false, identity: true),
                        Description = c.String(),
                        IsNew = c.Boolean(nullable: false),
                        Discount = c.Int(nullable: false),
                        IsActive = c.Boolean(nullable: false),
                        Name = c.String(nullable: false),
                        Category_Id = c.Long(nullable: false),
                    })
                .PrimaryKey(t => t.Id)
                .ForeignKey("dbo.Categories", t => t.Category_Id, cascadeDelete: true)
                .Index(t => t.Category_Id);

DTOS:

public class Product : Entity
    {
        public string Description { get; set; }
        public bool IsNew { get; set; }
        public int Discount { get; set; }
        [DefaultValue(true)]
        public bool IsActive { get; set; }
        public virtual ICollection<ProductSale> Sales { get; set; }
        [Required]
        public virtual Category Category { get; set; }
        public virtual ICollection<Photo> PhotoCollection { get; set; }
        public virtual ICollection<Shop> Shops { get; set; }
    }

public class ProductSale : Entity
    {
        public bool IsActive { get; set; }
        public int Price { get; set; }
        public string Description { get; set; }
        public virtual ICollection<Photo> PhotoCollection { get; set; }
        public virtual Product Product { get; set; }
    }

public class Entity
{
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
}

Application mostly works fine. but have one annoying issue. For cases 1-MANY, as above, DD will build "view productsales" with the link

/ProductSales/List.aspx?Product.Id=1 - which is incorrect, I think.

I have tried to play with url and found /ProductSales/List.aspx?Id=1 - it works.

UPDATE: error I have:

DataBinding: 'System.Web.DynamicData.Util.DictionaryCustomTypeDescriptor' does not contain a property with the name 'Product'. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.Web.HttpException: DataBinding: 'System.Web.DynamicData.Util.DictionaryCustomTypeDescriptor' does not contain a property with the name 'Product'.

I know that I can go to ChildrenField template and do some hack magic with ChildrenPath, but hope to get sweet and clear solution from community, thanks in advance.

Was it helpful?

Solution 2

Okay, I've spent few days reading looking for the answer, and,finally, found it.

As I sad I'm using EF CF approach.

Generation of InitialSchema is a tricky thing in CF.

I have applied some changes to my POCO classes:

public class Product : Entity
{
    public string Description { get; set; }
    public bool IsNew { get; set; }
    public int Discount { get; set; }
    [DefaultValue(true)]
    public bool IsActive { get; set; }

    [Required]
    public string Name { get; set; }

    public int CategoryId { get; set; }
    [Required]
    public virtual Category Category { get; set; }

    public virtual ICollection<Photo> Photos { get; set; }
    public virtual ICollection<Shop> Shops { get; set; }
    public virtual ICollection<Sale> Sales { get; set; }
}

public class Sale : Entity
{
public bool IsActive { get; set; }
public int Price { get; set; }
public string Description { get; set; }

    [Required]
    public string Name { get; set; }

    public int ProductId { get; set; } <-- added FK
    public virtual Product Product { get; set; }

    public virtual ICollection<Photo> Photos { get; set; }
}

EF CF is smart enough to understand dependencies and build appropriate relations, our goal is just to help CF do it correctly.

  • One moment, CF uses naming convention FK = /TableName here/Id
  • Does not care how to write Id, ID, id - CF will ignore casing.
  • I added explicit FKs to each entity I have, CategoryId, etc.

It's not enough, next point: I have Entity class, which for now is very simple:

public class Entity
{
        // no attr here.
    public int Id { get; set; }
}

Don't need to add Identity option as attribute, as I did in before(see origin question).

CF uses convention and treats Id as key, by default.

So, I have rebuild my Initial schema using

Enable-Migrations -ProjectName "MyProject"; add-migration InitialSchema -ProjectName "MyProject.Contracts"

then

Update-Database -ProjectName "MyProject.Contracts" -StartUpProjectName "MyProject.Web" -ConnectionStringName "MyProjectContext"

as result, I have correct URLs for one-to-many

/Sales/List.aspx?ProductId=1

My head was blowing about above, but finally it's solved, hope it will help you to save time. Thanks

OTHER TIPS

This will permanently fix this issue by modifying your ForeignKey.ascx.cs:

Replace Page_Init with:

    protected void Page_Init(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {                
            // Set the initial value if there is one
            string initialValue = AltDefaultValue ?? DefaultValue;
            if (!String.IsNullOrEmpty(initialValue))
                DropDownList1.SelectedValue = initialValue;
        }
    }

    private string AltDefaultValue
    {
        get
        {
            var foreignKeyNames = Column.ForeignKeyNames;
            if (foreignKeyNames.Count != 1) return null;

            object value;
            if (DefaultValues.TryGetValue(foreignKeyNames.Single(), out value))
                return Misc.ChangeType<string>(value);
            return null;
        }
    }

Also modify your ForeignKey_Edit.ascx:

Replace OnDataBinding with:

    public string AltGetSelectedValueString()
    {
        MetaForeignKeyColumn column = (MetaForeignKeyColumn)Column;
        foreach (string foreignKeyName in column.ForeignKeyNames)
            return Misc.ChangeType<string>(DataBinder.GetPropertyValue(Row, foreignKeyName));
        return null;
    }

    protected override void OnDataBinding(EventArgs e) {
        base.OnDataBinding(e);

        string selectedValueString = AltGetSelectedValueString();
        ListItem item = DropDownList1.Items.FindByValue(selectedValueString);
        if (item != null) {
            DropDownList1.SelectedValue = selectedValueString;
        }

    }

Once this is done it will use the AltDefaultValue for the simple case (in which DD EF6 fails!, and use the original code when a value isn't found).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top