To begin with, no I'm not using an ORM, nor am I allowed to. I have to hand-roll my repositories using ADO.NET.
I have two objects:
public class Firm
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual IEnumerable<User> Users { get; set; }
}
public class User
{
public Guid Id { get; set; }
public string Username { get; set; }
public Firm Firm { get; set; }
}
note the references to each other, a Firm has a list of Users, each User has only one Firm.
Now I want to design my repositories:
public interface IFirmRepository
{
IEnumerable<Firm> FindAll();
Firm FindById(Guid id);
}
public interface IUserRepository
{
IEnumerable<User> FindAll();
IEnumerable<User> FindByFirmId(Guid firmId);
User FindById(Guid id);
}
So far, so good. I want to load the Firm for each User from my UserRepository. The FirmRepository knows how to create a Firm from persistence, so I'd like ot keep that knowledge with the FirmRepository.
public class UserRepository : IUserRepository
{
private IFirmRepository _firmRepository;
public UserRepository(IFirmRepository firmRepository)
{
_firmRepository = firmRepository;
}
public User FindById(Guid id)
{
User user = null;
using (SqlConnection connection = new SqlConnection(_connectionString))
{
SqlCommand command = connection.CreateCommand();
command.CommandType = CommandType.Text;
command.CommandText = "select id, username, firm_id from users where u.id = @ID";
SqlParameter userIDParam = new SqlParameter("@ID", id);
command.Parameters.Add(userIDParam);
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
user = CreateListOfUsersFrom(reader)[0];
}
}
}
return user;
}
private IList<User> CreateListOfUsersFrom(SqlDataReader dr)
{
IList<User> users = new List<User>();
while (dr.Read())
{
User user = new User();
user.Id = (Guid)dr["id"];
user.Username = (string)dr["username"];
//use the injected FirmRepository to create the Firm for each instance of a User being created
user.Firm = _firmRepository.FindById((Guid)dr["firm_id"]);
}
dr.Close();
return users;
}
}
now when I go to load any User via the UserRepository, I can ask the FirmRepository to build the User's Firm for me. So far, nothing too crazy here.
Now the problem.
I want to load a list of Users from my FirmRepository. The UserRepository knows how to create a User from persistence, so I'd like to keep that knowledge with the UserRepository. So, I pass in a reference to IUserRepository to the FirmRepository:
public class FirmRepository : IFirmRepository
{
private IUserRepository
public FirmRepository(IUserRepository userRepository)
{
}
}
But now we have a problem. The FirmRepository depends on an instance of IUserRepository, and the UserRepository now depends on an instance of IFirmRepository. So one Repository cannot be created without an instance of the other.
If I keep IoC containers OUT of this equation (and I should, b/c Unit Tests should not use IoC containers), there is no way for me to accomplish what I'm trying to do.
No problem, though, I'll just create a FirmProxy to lazy-load the Users collection from Firm! That's a better idea, b/c I don't want to load ALL the Users all the time when I go to get a Firm or a list of Firms.
public class FirmProxy : Firm
{
private IUserRepository _userRepository;
private bool _haveLoadedUsers = false;
private IEnumerable<User> _users = new List<User>();
public FirmProxy(IUserRepository userRepository)
: base()
{
_userRepository = userRepository;
}
public bool HaveLoadedUser()
{
return _haveLoadedUsers;
}
public override IEnumerable<User> Users
{
get
{
if (!HaveLoadedUser())
{
_users = _userRepository.FindByFirmId(base.Id);
_haveLoadedUsers = true;
}
return _users;
}
}
}
So, now I have a nice proxy object to facilitate lazy-loading. So, when I go to create a Firm in the FirmRepository from persistence, I instead return a FirmProxy.
public class FirmRepository : IFirmRepository
{
public Firm FindById(Guid id)
{
Firm firm = null;
using (SqlConnection connection = new SqlConnection(_connectionString))
{
SqlCommand command = connection.CreateCommand();
command.CommandType = CommandType.Text;
command.CommandText = "select id, name from firm where id = @ID";
SqlParameter firmIDParam = new SqlParameter("@ID", id);
command.Parameters.Add(firmIDParam);
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
firm = CreateListOfFirmsFrom(reader)[0];
}
}
}
return firm;
}
private IList<Firm> CreateListOfFirmsFrom(SqlDataReader dr)
{
IList<FirmProxy> firms = new List<FirmProxy>([need an IUserRepository instance here!!!!]);
while (dr.Read())
{
}
dr.Close();
return firms;
}
But this is still not working!!!
In order to return a FirmProxy instead of a Firm, I need to be able to new up a FirmProxy in my FirmRepository class. Well, the FirmProxy takes an IUserRepository instance b/c the UserRepository contains the knowledge of how to create a User object from persistence. As a result of the FirmProxy needing an IUserRepository, my FirmRepository now needs an IUserRepository as well, and I'm right back to square one!
So, given this long winded-explanation/source code, how can I go about being able to create an instance of a User from the FirmRepository and an instance of Firm from the UserRepository without:
I think about NHiberante and Enitity Framework, and it seems they have no problem figuring out how to generate sql for a simple example that I've presented.
Does anyone else have any other ideas/methods/etc... that will help me achieve what I want to do without an ORM?
Or maybe there is a different/better way to approach this? Want I don't want to lose is the ability to access a Firm from a User, or to get a list of Users for a given Firm
But circular dependencies in software are solvable because the dependencies are always self-imposed by the developers. To break the circle, all you have to do is break one of the links. One option might simply be to come up with another way to produce one of the dependencies, in order to bootstrap the process.
To resolve circular dependencies: Then there are three strategies you can use: Look for small pieces of code that can be moved from one project to the other. Look for code that both libraries depend on and move that code into a new shared library. Combine projectA and projectB into one library.
A simple way to break the cycle is by telling Spring to initialize one of the beans lazily. So, instead of fully initializing the bean, it will create a proxy to inject it into the other bean. The injected bean will only be fully created when it's first needed.
Therefore, you experience circular dependency only once you have created the second column. The correct solution to avoid this is to restrict the list of columns that the calculated column depends on, by using ALLEXCEPT or REMOVEFILTERS and keeping only the table's primary key.
You need to think more clearly about your aggregate root objects, i.e. the focus of each repository. You don't create a repository for each table in your database, that's not the objective. The intent is to identify the aggregate root object that you need to work with, include the child tables/records, i.e. User(Firm, Address, Access Rights). That's what your repository will return to your app.
The difficulty you are having is showing you that your repositories are not correctly structured. Anytime I find my code becoming too difficult it raises an alert with me that I'm probably doing it wrong, I live my life by that ;)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With