ForewordIn my first post on Habré, the following dialogue began:
A. It is important that the database cannot be accessed directly, only through stored-procedures, and a specific class is responsible for calling the stored procedure.
')
B. “only through stored procedures” - what do you say to NoSQL products?
Further emphasis shifted SQL vs. NoSQL. But the basics were lost: the
work with the base should be organized through a specially sharpened class, and not spread the call code throughout the project .
I still think that NoSQL is too young products to compete with relational databases in all seriousness. But NoSQL has a slightly different niche. I needed some data storage in the project, where there are no large volumes. And so I decided to try
MongoDB . (I'd rather work with Oracle NoSQL Database, but I didn’t find how to work with it on C #).
Well, in general, everything is good enough to save the object in the database, it turned out that very little needs to be done:
var collection = db. GetCollection < StrategiesData > ( ) ;
collection. Save ( argObject ) ;
where StrategiesData is the type of my object, argObject is actually my object. But this style encourages scattering just the appeal to the database throughout the project. Interfere with the explicit indication of the object
< StrategiesData >
Well, what remains is the mapping (upd. It turns out that there is another option - just to continue thinking in the framework of generalized programming, but through the mapping it turns out to be more reliable). About this and talk.
Display of generalized methodsMapping generalized methods turned out to be not so easy. So I decided to share, can someone come in handy.
public class Database
{
/// Current database
IMongoDatabase db ;
public void Save ( object argObject )
{
MethodInfo MethodGetCollectionGeneric = null ;
Type myType = typeof ( IMongoDatabase ) ;
MethodInfo [ ] myMethod = myType. GetMethods ( ) ;
foreach ( MethodInfo m in myMethod )
{
// Select only methods with the name GetCollection
if ( m. Name == "GetCollection" )
{
ParameterInfo [ ] pi = m. GetParameters ( ) ;
// Only interested in such overloads, where the returned type is generalized,
// method has no parameters
if ( m. ReturnType . IsGenericType && pi. Length == 0 )
{
MethodGetCollectionGeneric = m ;
break ;
}
}
}
// Get the type of our object
Type ObjectType = argObject. GetType ( ) ;
Type [ ] typeArgs = { ObjectType } ;
// Instantiate our generic method
MethodInfo MethodGetCollectionGenericMake = MethodGetCollectionGeneric. MakeGenericMethod ( typeArgs ) ;
// execute the method
var collection = MethodGetCollectionGenericMake. Invoke ( db, null ) ;
collection. Save ( argObject ) ;
}
Total we have a simpler reference to the method of conservation. And everywhere we work with our Database class object. And when you need to work with other databases, including relational ones, this is easily changed by setting the Database class, and the call will remain the same. Otherwise, I would have to change the code for the entire project, which in a serious project is similar to suicide. Therefore, never scatter the code to access a specific project database - for this, a special class must be allocated (or their hierarchy, and then the “task manager” - your main class at the top decides where to save by selecting one or another heir from your Database)
upd.I was prompted below, it is all the easier if you think
public void Save2 < T > ( T argObject ) where T : class
{
var collection = db. GetCollection < T > ( ) ;
collection. Save ( argObject ) ;
}
But "warm up" :)
upd 2. Nevertheless, the solution above without mapping is not always successful! When working with the inheritance hierarchy, the type is not determined correctly. Without mapping, the type is assumed to be the parent of the object, not the object itself. A little later, lay out the code to make it clear.
upd 3.More advanced article -
Separate database logic (attempt # 2)