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
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 DatabaseCREATE 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>
I wouldn't bother with trying to get the ibatis mapper to support "getter" methods using your custom collection. Just implement the constructor:
public SqlDataItemCollection(IEnumberable<T> itemList) :base(itemList) { }
And in your DAO method, just do:
public SqlDataItemCollection<foo> GetFooList()
{
SqlDataItemCollection<foo> fooList=new SqlDatItemCollection<foo>(mapper.QueryForList<foo>(null));
return foo;
}
tRi11
ASKER
Hi,
Thanks for this, do I assume then that it is not possible to make iBATIS.NET populate the list from the mapping file, i.e. when a query is run to get Foo it automatically populates all the associated Bars as well?
At the moment I'm just trying to get a feel for iBATIS.NET and so am not worried about performance issues or lazy loading etc. However, I do want to be able to retrieve a particular Foo fully populated with all associated Bars.
Thanks,
Paul
tRi11
ASKER
Hi,
The problem seems to simply relate to the fact I need to use an interface instead of a concrete class. Replacing IDataItemCollection<Bar> with SqlDataItemCollection<Bar> in Foo causes everything to work as expected, unfortunately I then loose the ability to swap the concrete classes with Unity.
but there is nothing limiting you to downgrade the return value back to its implementing interface in the DAO method:
public IDataItemCollection<T> GetFooList<T>() { SqlDataItemCollection<T> fooList=new SqlDatItemCollection<T>(mapper.QueryForList<foo>(null)); return foo; }or if your select element is using the ListClass attributepublic IDataItemCollection<T> GetFooList<T>() { return (SqlDatItemCollection<T>)(mapper.QueryForList<foo>(null)); }
Thanks again. Yes the listClass attribute does exactly what I want but unfortunately it only appears to work on concrete classes. The problem is that I do not want a dependency to SqlDataItemCollection<T>, this should be resolved by Unity at runtime. I also need for the Foo object to be returned fully populated, I know I can perform a QueryForList to populate the Bars collection of Foo but this then relies on a specific call in the DAO not just the appropriate mapping in the XML file.
It kind of looks like I am trying to do something that can't be done with iBATIS.NET and I either need to look at a different ORM or review the domain model.
Thanks for the suggestion, I think you're probably right, it looks like creating a custom implementation is going to be the only way of getting it to work the way I want it to. I was hoping the solution would be fairly straight forward, at the moment I'm not prepared to spend the time trying to fit iBATIS.NET around my domain model when there are so many other ORM's out there which may fit better.
I will leave this question open for now as I may come back to it if nothing works out better. If I decide to go with another ORM I will award you the points as my question was for iBATIS.NET and although I'm not prepared to try it now, I do believe your suggestion offers the best chance of success.
I have decided to drop IBATIS.NET as the main ORM for my project. I have opted instead for a lightweight attribute based ORM that works perfectly with my current domain design and goals for the project as a whole. Anyway, thanks for your help, I will award you the points as I'm sure your solution was the way to go had I decided to stick to IBATIS.NET.
Thanks,
Paul
tRi11
ASKER
I did not choose to adopt the solution myself instead opting for an alternate ORM, however the solution appears to be the best option if sticking with IBATIS.NET.
public SqlDataItemCollection(IEnu
And in your DAO method, just do:
public SqlDataItemCollection<foo>
{
SqlDataItemCollection<foo>
return foo;
}