In this article, PersistentObjects, Einar Ingebrigtsen writes about object-relational persistence using custom attributes on the classes and properties.
But I think that an O/R mapper should not require anything from its client objects that are to be persistent, neither to be sub-classes of ContextBoundObject, nor to be decorated with persistent oriented attributes. Besides, objects to be persistent do not necessarily know the persistent targets. I tried using interfaces to de-couple the objects and persistent rules.
For the interface like this,
[PersistentTarget("Users")]
public interface IUserToUpdateOrDelete
{
[PersistentField("Name", "VarChar", 50, false, IsPrimaryKey=true)]
string UserName{get; set;}
[PersistentField("DealerId", "int", 8, false, IsPrimaryKey=true)]
string Dealer{get; set;}
[PersistentField("Application", "VarChar", 50, false)]
string Application{get; set;}
}
SqlObjectMapper.CreateUpdateSqlStatementTemplate(typeof(IUserToUpdateOrDelete)) generates SQL statement like,
update [Users] set Name=@Name,DealerId=@DealerId,Application=@Application where Name=@Name and DealerId=@DealerId
It can create SQL statements for insert, delete and select in a similar way. Besides these, it can also generate an array of SqlParameter form the object instances and with the interfaces.
public class User : IUserToList, IUserToCreate, IUserToUpdateOrDelete
{
......
}
User user = new User();
user.UserName = "test user";
SqlParameter[] sqlParameters = SqlObjectMapper.CreateSqlParameters(user, typeof(IUserToCreate));
The above line returns SqlParameter array to work with the generated SQL statements. Therefore the object is for data and the interface is for persistent rules.
Finally, a simple O/R mapping utility can do something like this,
public void Insert(object obj,System.Type type)
{
SqlObjectMapper sqlObjectMapper = new SqlObjectMapper(obj,type);
string sql= sqlObjectMapper.CreateInsertSqlStatementTemplate();
SqlParameter[] sqlParameters = sqlObjectMapper.CreateSqlParameters();
SqlHelper.ExecuteNonQuery(CommandType.Text,sql, sqlParameters);
}
Now, how about schema support? Parsing schema XML, then use Code DOM to emit DLLs of interfaces is not a big problem. Like how does xsd generate typed dataset.
Things even goes wild if to emit SQL statements generating and SQL parameter generating algorithms into DLLs. From the source code of XML-RPC.NET, the XmlRpcProxyGen.cs shows a lot of hints of dynamically creating DLLs.