It's always more fun to look at implementation than to simply talk about things at a high-level overview. With that said, I'm going to dive into a simple two-type domain model example.
Classes First
1: [PersistenceStateAspect]
2: public class Person : PersistenceState
3: { 4: public string FirstName { get; set; } 5: public string LastName { get; set; } 6: public DateTime DateOfBirth { get; set; } 7: public string SocialSecurity { get; set; } 8: public List<Vehicle> Vehicles{ get; set; } 9: }
10:
11: [PersistenceStateAspect]
12: public class Vehicle : PersistenceState
13: { 14: public string Vin { get; set; } 15: public string Model { get; set; } 16: public int Year { get; set; } 17: public string OwnerSocial { get; set; } 18: }
Hopefully that's so simple you're bored. Note that Person has a property of List<Vehicle>, that's where our inheritance comes into play.
I'm sure you've noticed the only two things that set these classes apart: the attribute and the base class. Once upon a time, the base class was unnecessary but due to the WCF support, it's now a fixture. There were exceptions at one time, but certain constraints on the DatabaseRepository now prevent code from compiling if you try to use DatabaseRepository against types that don't inherit from IPersistable. The silver lining is that you can implement IPersistable however you like.
Now For The Maps
For the sake of brevity, I've put both .Xml binding maps for each types in a single listing. Normally each of these would be a separate file.
1: <map target="Nvigorate.Test.TestObjects.Person, Nvigorate.Test" source="simple">
2: <sources>
3: <source name="Person" root="true" >
4: <assignment target="SocialSecurity" source="SocialSecurity" targetKey="true" sourceKey="true" />
5: <assignment target="FirstName" source="FirstName" />
6: <assignment target="LastName" source="LastName" />
7: <assignment target="DateOfBirth" source="DateOfBirth" />
8: </source>
9: </sources>
10: <relatesTo>
11: <relationship type="parent" relative="Nvigorate.Test.TestObjects.Vehicle, Nvigorate.Test">
12: <constraint subjectField="SocialSecurity" relativeField="OwnerSocial" />
13: </relationship>
14: </relatesTo>
15: </map>
16:
17: <map target="Nvigorate.Test.TestObjects.Vehicle, Nvigorate.Test" source="simple">
18: <sources>
19: <source name="Vehicle" root="true" >
20: <assignment target="VIN" source="vehicleId" targetKey="true" sourceKey="true" />
21: <assignment target="OwnerSocial" source="ownerSSN" />
22: <assignment target="Model" source="model" />
23: <assignment target="Year" source="year" />
24: </source>
25: </sources>
26: </map>
Let's briefly discuss some of the elements here. The use of 'source' in different parts of the map may be confusing. Source in the map element refers to the repository identifier (in our case, the name of the database connection string). In the sources collection, each source element (in this context) represents how a table relates to the type. Each assignment relates a property to a column name. The relatesTo block describes how one type relates to another type in terms of the properties of the two types.
Examples
If we assume that the MapIndex has been configured to load .xml maps in a given directory and these two are in that path, the following examples would be working code. So you can see just how easy it is to configure the Nvigorate, here's an example of the .config
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <configSections>
4: <section name="MapConfiguration" type="Nvigorate.Relational.Binding.MapConfigurationSection, Nvigorate, Version=0.6.0.0, Culture=neutral, PublicKeyToken=e95d15436b84113a" />
5: </configSections>
6:
7: <connectionStrings>
8: <add name="simple" connectionString="Data Source=localhost; Initial Catalog=nvigorate; Integrated Security=true;" providerName="Nvigorate.Data.MSSql8Interface, Nvigorate"/>
9: </connectionStrings>
10:
11: <MapConfiguration>
12: <scanTargets>
13: <scan directory="C:\TFS\Nvigorate\Release\Nvigorate.Test\Maps"/>
14: </scanTargets>
15: </MapConfiguration>
16:
17: </configuration>
From this point on, the code I'm going to show you is all the code you need. There's no additional setup, initialization or configuration required in order to starting using the code.
Example 1 - Loading A Single Instance Of A Single Type
1: var repository = new DatabaseRepository("simple"); 2:
3: //load a person instance by primary key (social security)
4: var person1 = repository.GetInstance<Person>("321-12-1234"); 5:
6: //load a vehicle instance by primary key (VIN)
7: var car1 = repository.GetInstance<Vehicle>("10SKJUWRP1NMAVCNM"); 8:
9: //load first person instance matching critieria
10: var person2 = repository.GetInstance<Person>(
11: new LoadInstuction<Person>()
12: .Where(Criterion.Property("FirstName").Like((Literal)"Dav%"); 13:
14: //load first person instance matching critieria
15: var car2 = repository.GetInstance<Vehicle>(
16: new LoadInstruction<Vehicle>()
17: .Where(Criterion.Property("Year").Between((Literal)1990).And((Literal)1995);
The important thing to note about this is that the person instances will NOT contain any vehicle instances because we didn't specify that that was the desired behavior.
Example 2 - Loading A Collection Of A Single Type
1: var repository = new DatabaseRepository("simple"); 2:
3: // load the first 5 records of each type matching
4: // the criteria provided
5: var people = repository.GetCollection<Person>(
6: new LoadInstruction<Person>()
7: .Page(1)
8: .PageBy(5)
9: .Where(
10: Criterion.Property("DateOfBirth") 11: .Between((Literal)"01/01/1979").And((Literal)"12/31/1979") &
12: Criterion.Property("LastName") 13: .Like("Rob%"));
Example 3 - Loading A Collection With Children
1: var repository = new DatabaseRepository("simple"); 2:
3: // load the first 5 records of each type matching
4: // the criteria provided
5: var people = repository.GetCollection<Person>(
6: new LoadInstruction<Person>()
7: .Page(1)
8: .PageBy(5)
9: .Where(
10: Criterion.Property("DateOfBirth") 11: .Between((Literal)"01/01/1979").And((Literal)"12/31/1979") &
12: Criterion.Property("LastName") 13: .Like("Rob%")) 14: .EagerLoad();
Notice that the only difference between this query and the previous one is the added .EagerLoad() call. This let's Nvigorate know that the consumer wants the entire domain model's records fetched and loaded.
Example 4 - Loading A Collection With Limited Population
We've run into several scenarios where we only need part of the information available on an entity. Instead of spending additional bandwidth and cycles populating the entire object, why not provide a way to allow consumers with the ability to specify which properties they want?
1: var repository = new DatabaseRepository("simple"); 2:
3: // load the first 5 records of each type matching
4: // the criteria provided
5: var people = repository.GetCollection<Person>(
6: new LoadInstruction<Person>()
7: .Page(1)
8: .PageBy(5)
9: .Where(
10: Criterion.Property("DateOfBirth") 11: .Between((Literal)"01/01/1979").And((Literal)"12/31/1979") &
12: Criterion.Property("LastName") 13: .Like("Rob%") 14: .LoadOnly("FirstName","LastName") 15: );
Example 5 - Creation, Update and Deletion Of An Instance
Nvigorate utilizes AOP to handle state tracking. This means several things for you, the user of Nvigorate:
1) dirty tracking happens without any additional code on your part
2) persistence is handled automatically for you, you don't need to know what operations need to take place
3) Nvigorate handles "unit of work" and transactional behavior so you don't have to worry about orphans
1: var repository = new DatabaseRepository("simple"); 2:
3: // create a new person record
4: var newRecord = new Person ()
5: { 6: SocialSecurity = "401-10-1093",
7: FirstName = "Bilbo",
8: LastName = "Baggins",
9: DateOfBirth = DateTime.Parse("01/01/1542") 10: };
11:
12: // save it
13: repository.Perist(newRecord);
14:
15: // now load it back this is to demonstrate that you can
16: // load a fresh instance and update from it
17: var loadedRecord = repository.GetInstance<Person>("401-10-1093"); 18: loadedRecord.FirstName = "Frodo";
19: repository.Persist(loadedRecord);
20:
21: // now delete a record
22: repository.Delete(loadedRecord);
There's more. A lot more actually. Hopefully these examples provide you with enough concrete implementation that your interest in Nvigorate is increasing! Feed back is welcomed but only if it's not along the lines of "Hay, y dun u yewz NH1b3Rn4tz0rz?! it R teh b3st ORMz!" (can you tell I've heard that a lot?)
Tags: