igor-tkachev / bltoolkit

Business Logic Toolkit for .NET
MIT License
297 stars 112 forks source link

Mapping is broken? #406

Open AlexLook opened 5 years ago

AlexLook commented 5 years ago

Hi! Sorry for very bad english. Strange situation. On all project all OK, but some code work wrong/ After second call mapping don't work. For sample come classes:

[Serializable]
public class ItemGroup {
    public int    Id   { get; set; }
    public string Name { get; set; }
}

[Serializable]
public class ItemType {
    public int       Id        { get; set; }
    public string    Name      { get; set; }
    public ItemGroup ItemGroup { get; set; }  <--- = null on second call

    public ItemType() {
        ItemGroup = new ItemGroup();
    }
}

[Serializable]
public class Item {
    public int      Id       { get; set; }
    public string   Name     { get; set; }
    public ItemType ItemType { get; set; }  // <--- = null on second call
    // …
    [MapIgnore] public string ItemTypeName  { get { return ItemType.Name; } }
    [MapIgnore] public string ItemGroupName { get { return ItemType.ItemGroup.Name; } }
    // …
    public Item() {
        ItemType = new ItemType();
    }
}

In ItemRepository:

public class ItemRepository {

    private const string GetItemsQuery = @"
        SELECT i.Id     AS [Id],
               i.Name   AS [Name],
               t.Id     AS [ItemType.Id],
               t.Name   AS [ItemType.Name],
               g.Id     AS [ItemType.ItemGroup.Id],
               g.Name   AS [ItemType.ItemGroup.Name]
        FROM   Items i
               JOIN ItemTypes t
                    JOIN ItemGroups g
                      ON t.GroupId = g.Id
                 ON i.TypeId = t.Id
        WHERE  g.Id = @groupId
          AND  t.Id = @typeId";

    public List<Item> GetItems(int groupId, int typeId) { // <- on second call this method marked fields is null
        using (var db = new DbManager()) {
            return db.SetCommand(GetItemsQuery,
                                 db.Parameter("@groupId", groupId),
                                 db.Parameter("@typeId",  typeId))
                     .ExecuteList<Item>();
        }
    }
}

On code

private void ShowData(int groupId, int typeId) {
    var items = itemsRepository.GetItems(groupId, typeid);
    itemsBindingSource.DataSource = new BindingListImpl(Filter(items), typeof(Item));    
}

On first call ShowData() all fields have good data (Item.ItemType != null, Item.ItemType.ItemGroup != null), but after second and next calls Item.ItemType=null and ItemType.ItemGroup =null, but in database all OK. Second and next calls db.ExecuteList() return invalid object with null properies. Query in database is correct and return correct data, Behaviour like not called default constructor (without parameters) on ItemType and ItemGroup or broken mapping. In other parts of program all correct, but two or three methods is broken.
BLToolkit last version. .NetFramework 4.0.

ili commented 5 years ago

@AlexLook sorry for really late responce, but projet is suspended.

Speaking about your case it is really strange, if no factories are used objects would be initialized with default constructors. During the mapping only primitive properties are set, so null value could not be set for the ItemType.ItemGroup property, so i'm confused little bit.

I can advice you trying to specify more detailed mapping configuration with MapFieldAttibute:

[Serializable]
public class ItemGroup {
    public int    Id   { get; set; }
    public string Name { get; set; }
}

[Serializable]
public class ItemType {
    public int       Id        { get; set; }
    public string    Name      { get; set; }
    [MapField(Format="ItemGroup.{0}"]
    public ItemGroup ItemGroup { get; set; }  //<--- = null on second call

    public ItemType() {
        ItemGroup = new ItemGroup();
    }
}

[Serializable]
public class Item {
    public int      Id       { get; set; }
    public string   Name     { get; set; }
    [MapField(Format="ItemType.{0}"]
    public ItemType ItemType { get; set; }  // <--- = null on second call
    // …
    [MapIgnore] public string ItemTypeName  { get { return ItemType.Name; } }
    [MapIgnore] public string ItemGroupName { get { return ItemType.ItemGroup.Name; } }
    // …
    public Item() {
        ItemType = new ItemType();
    }
}

More detailed at: https://github.com/igor-tkachev/bltoolkit/wiki/Mapping.MapFieldAttribute