views:

800

answers:

1

Hi,

I'm trying to use a generic custom collection interface (to support injection with Microsoft Patterns and Practices Unity) in a class O/R mapped with iBATIS.NET. Does anyone know if this is possible and if so how to do it?

I have an IDataItemCollection<T> interface that I map to SqlDataItemCollection<T> which extends CollectionBase. I want to use the IDataItemCollection<T> in my classes so I can swap SqlDataItemCollection<T> with other classes that extend the interface through Unity. The iBATIS.NET mapping file can reference the concrete class directly as there won't be one without the other.

Below I have included a very simplified example of the code, database and mappings. I'm completely new to iBATIS.NET and really just want to prove its use at the moment, so please re-jig the mapping XML as necessary.

Many thanks,

Paul


C# Code

public interface IDataItem
{
    object Id { get; set; }
}

public class DataItem : IDataItem
{
    public object Id { get; set; }
}

public interface IDataItemCollection<T> : ICollection where T : IDataItem
{
    // Various Getters and Setters
...
}

public class SqlDataItemCollection<T> : CollectionBase, IDataItemCollection<T> where T : DataItem
{
    public SqlDataItemCollection() { }
    public SqlDataItemCollection(T injType) { }

    // Getters and Setters to implement interfaces
...
}

public class Foo : DataItem
{
    public Foo(IDataItemCollection<Bar> bars)
    {
        Bars = bars;
    }

    public IDataItemCollection<Bar> Bars { get; set; }
}

public class Bar : DataItem { }

SQL Server 2005 Database

CREATE TABLE Foo
(
    Id bigint IDENTITY(1,1)
)

CREATE TABLE Bar
(
    Id bigint IDENTITY(1,1)
)

CREATE TABLE FooBar
(
    FooId bigint,
    BarId bigint
)

iBATIS.NET mapping.xml

<resultMaps>
    <resultMap id="FooResult" class="Foo">
        <result property="Id" column="Id"/>
        <result property="Bars" column="Id" select="SelectBarsInFoo" lazyLoad="false"/>
    </resultMap>

    <resultMap id="BarResult" class="Bar">
        <result property="Id" column="Id"/>
    </resultMap>
</resultMaps>

<statements>
    <select id="SelectFoo" resultMap="FooResult">
        SELECT Id
        FROM Foo
    </select>

    <select id="SelectBarsInFoo" parameterClass="long" resultMap="BarResult" listClass="SqlDataItemCollection`1[Bar]" >
        SELECT Bar.Id
        FROM Bar
        JOIN FooBar ON Bar.Id = FooBar.BarId
        WHERE FooBar.FooId = #value#
    </select>
</statements>
A: 

I just had this same problem after refactoring a portion of my application back to interfaces. I got around it by explicitly implementing the interface's definition of my collection and then duplicating the implementation as its concrete class. This may not solve your problem.

public interface IGroup { }
public class Group : IGroup { }
public class IGroupCollection : IList<IGroup> { }
public class GroupCollection : IGroupCollection { }

public interface IConcrete
{
    IGroupCollection Items { get; set; }
}

public class Concrete : IConcrete
{
    public GroupCollection Items { get; set; }
    IGroupCollection IConcrete.Items
    {
        get { return Items; }
        set { Items = value as GroupCollection; }
    }
}

This allows iBATIS.NET to add items to the collection without running into a type conversion error while the explicit interface implementation allows me to use the IConcretes throughout my app without referring to the actual Concrete or actual GroupCollection.

cfeduke