joshua.ball
06-18-2008, 01:11 PM
At first glance, this post might look like an nhibernate question, but I believe
the error message I am receiving has more to do with AOP
then it does with nhibernate. I am presenting this in the context of nhibernate because people are familiar with it and can easily see the practical benefit of what I am trying to do.
Here is my problem. Instead of making calls like this:
IList<Employee> = myService.GetEmployeesByLocation(locationId);
IList<Location> = myService.GetLocationsByCountry(countryId);
IList<Employee> = myService.GetEmployeesByDepartment(departmentId);
I want to be able to make calls like this:
IList<Employee> = myService<Employee>.GetBy<Location>(locationId);
IList<Location> = myService<Location>.GetBy<Country>(countryId);
IList<Employee> = myService<Employee>.GetBy<Department>(departmentId);
The reason is obvious. In the first case I have to write 3 methods in my service layer. In the second I only have to write one.
Here is my proposed solution:
public class MyService<TObject> {
public IList<TObject> GetBy<TObj, A>(int objectId) where TObj : TObject, IGetBy<A>
{
if (objectId <= 0) return null;
ICriteria criteria = Session.CreateCriteria(typeof(TObject));
string property = typeof(A).Name;
criteria.Add(new EqExpression(property + ".Id", objectId));
return criteria.List<TObject>();
}
}
That type signature may be a little confusing. What I really want to say is this:
public IList<TObject> GetBy<A>(int objectId) where TObject : IGetBy<A>
but that does not compile. =/ Thus the additional TObj type parameter, which I always pass in as the same type as TObject. So my calls actually end up looking like this:
IList<Employee> = myService<Employee>.GetBy<Employee,Location>(locationId);
IList<Location> = myService<Location>.GetBy<Location,Country>(countryId);
IList<Employee> = myService<Employee>.GetBy<Employee,Department>(countryId);
To prevent non-sense queries like the following:
IList<Employee> = myService<Employee>.GetBy<FruitSalad>(fruitSaladId);
I have created an empty interface called IGetBy`1, and implemented it on classes only where it makes sense.
public interface IGetBy<A> { /* empty interface */ }
public class Employee : IGetBy<Location>, IGetBy<Department> {
...
public virtual Location Location { get; set; }
...
public virtual Department Department { get; set; }
...
}
public class Location : IGetBy<Country> {
...
public virtual Country Country { get; set; }
...
}
A unit test verifies for me that all classes in the assembly which implement IGetBy`1 have a property whose name is the same as the type name. If the unit test passes, then I know that only valid EqExpression will be constructed in MyService.GetBy<,>(...).
Everything seemed clean and beautiful, until I started up the project, and I got the following error message:
System.TypeLoadException: Method 'GetBy' on type 'CompositionAopProxy_1f73a05af11343d3838a425296467 d99' from assembly 'Spring.Proxy, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' tried to explicitly implement an interface method with weaker type parameter constraints.
I assume it's talking about the following:
public IList<TObject> GetBy<TObj, A>(int objectId) where TObj : TObject, IGetBy<A>
Does Spring not support AOP across calls to methods with type parameter constraints? Or am I doing something wrong? If not, are there any work-arounds?
the error message I am receiving has more to do with AOP
then it does with nhibernate. I am presenting this in the context of nhibernate because people are familiar with it and can easily see the practical benefit of what I am trying to do.
Here is my problem. Instead of making calls like this:
IList<Employee> = myService.GetEmployeesByLocation(locationId);
IList<Location> = myService.GetLocationsByCountry(countryId);
IList<Employee> = myService.GetEmployeesByDepartment(departmentId);
I want to be able to make calls like this:
IList<Employee> = myService<Employee>.GetBy<Location>(locationId);
IList<Location> = myService<Location>.GetBy<Country>(countryId);
IList<Employee> = myService<Employee>.GetBy<Department>(departmentId);
The reason is obvious. In the first case I have to write 3 methods in my service layer. In the second I only have to write one.
Here is my proposed solution:
public class MyService<TObject> {
public IList<TObject> GetBy<TObj, A>(int objectId) where TObj : TObject, IGetBy<A>
{
if (objectId <= 0) return null;
ICriteria criteria = Session.CreateCriteria(typeof(TObject));
string property = typeof(A).Name;
criteria.Add(new EqExpression(property + ".Id", objectId));
return criteria.List<TObject>();
}
}
That type signature may be a little confusing. What I really want to say is this:
public IList<TObject> GetBy<A>(int objectId) where TObject : IGetBy<A>
but that does not compile. =/ Thus the additional TObj type parameter, which I always pass in as the same type as TObject. So my calls actually end up looking like this:
IList<Employee> = myService<Employee>.GetBy<Employee,Location>(locationId);
IList<Location> = myService<Location>.GetBy<Location,Country>(countryId);
IList<Employee> = myService<Employee>.GetBy<Employee,Department>(countryId);
To prevent non-sense queries like the following:
IList<Employee> = myService<Employee>.GetBy<FruitSalad>(fruitSaladId);
I have created an empty interface called IGetBy`1, and implemented it on classes only where it makes sense.
public interface IGetBy<A> { /* empty interface */ }
public class Employee : IGetBy<Location>, IGetBy<Department> {
...
public virtual Location Location { get; set; }
...
public virtual Department Department { get; set; }
...
}
public class Location : IGetBy<Country> {
...
public virtual Country Country { get; set; }
...
}
A unit test verifies for me that all classes in the assembly which implement IGetBy`1 have a property whose name is the same as the type name. If the unit test passes, then I know that only valid EqExpression will be constructed in MyService.GetBy<,>(...).
Everything seemed clean and beautiful, until I started up the project, and I got the following error message:
System.TypeLoadException: Method 'GetBy' on type 'CompositionAopProxy_1f73a05af11343d3838a425296467 d99' from assembly 'Spring.Proxy, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' tried to explicitly implement an interface method with weaker type parameter constraints.
I assume it's talking about the following:
public IList<TObject> GetBy<TObj, A>(int objectId) where TObj : TObject, IGetBy<A>
Does Spring not support AOP across calls to methods with type parameter constraints? Or am I doing something wrong? If not, are there any work-arounds?