Startup might look something like this:
var sessionFactory = Fluently.Configure .ConnectionString("Data source=MyDatabase.sqlite") .DatabaseAdapter(DbAdapter.Sqlite) .Domain(d => { d.IdConvention(x => x.EntityType.Name + "Id") .Access(Access.Property) .Generator(Generator.GuidComb); d.ListParentIdColumnNameConvention(x => x.ParentType.Name + "_id"); d.BelongsToColumnNameConvention(x => x.PropertyName + "_id"); d.Entity<Person>(e => { e.Property(x => x.FirstName); e.Property(x => x.LastName); e.Property(x => x.Active); e.Property(x => x.MemberSince); }); d.Entity<Forum>(e => { e.List(x => x.Posts).ParentIdColumn("PostedTo_id"); e.Property(x => x.Name); e.Property(x => x.TimeOfDayLastUpdated); }); d.Entity<Post>(e => { e.Property(x => x.Title); e.Property(x => x.Body); e.Property(x => x.DatePosted); e.BelongsTo(x => x.Poster).Column("Poster_id"); }); }) .Build(); UnitOfWork.Initialize(sessionFactory); using (UnitOfWork.Start()) { DatabaseMigrator.Execute(); }
Notice that you do not have to map the Id property for any entity; Catnap will map it automatically per convention. All other persisted properties must be mapped and must have both a getter and a setter. The getters and setters do not have to be public. Notice the conventions specified at the beginning of the domain mapping. These are optional, and if omitted Catnap's will apply default conventions.
There are some special considerations for using Catnap with MonoTouch.
The following code fetches a forum, changes its title, removes a post and adds a post, then saves the forum. The Posts collection is loaded lazily. Adds/deletes to Posts are cascaded when the forum is saved. At the start of the using block a connection is opened and a transaction started. At the end of the using block the transaction is committed (or rolled back in the case of an error) and the connection is closed.
using (var uow = UnitOfWork.Start()) { var person = uow.Session.Get<Person>(1); var forum = uow.Session.Get<Forum>(1); forum.Title = "Annoying Complaints"; forum.RemovePost(forum.Posts.First()); forum.AddPost(new Post { Title = "Please help!", Body "Now!", Person = person }); uow.Session.SaveOrUpdate(forum); }
Criteria provides a way to specify conditions for a query. Derive from Criteria class:
public class TimeEntriesCriteria : Criteria<TimeEntry> { public TimeEntriesCriteria Project(int projectId) { return Equal(x => x.Project, projectId); } public TimeEntriesCriteria Billed(bool billed) { return Equal(x => x.Billed, billed); } }
Usage:
var criteria = new TimeEntriesCriteria() .Project(projectId) .Billed(false) var unbilledTimeEntriesForProject = session.List(criteria);
Or build custom criteria as needed:
var criteria = Criteria.For<TimeEntry>() .Equal(x => x.Project, projectId) .Where(x => Date < new DateTime(2009, 1, 1); var oldTimeEntriesForProject = session.List(criteria);
The following unit test illustrates more complex criteria:
public class when_creating_a_complex_condition { static ICriteria<Person> criteria; static IDbCommandSpec target; static ISessionFactory sessionFactory; Establish context = () => { sessionFactory = Fluently.Configure .Domain(d => d.Entity<Person>(e => { e.Id(x => x.Id).Access(Access.Property); e.Property(x => x.FirstName); e.Property(x => x.LastName); e.Property(x => x.MemberSince); })) .Build(); criteria = Criteria.For<Person>() .Less("Bar", 1000) .GreaterOrEqual("Bar", 300) .Or(or => { or.NotEqual(x => x.FirstName, "Tim"); or.And(and => { and.Equal("Foo", 25); and.Equal("Baz", 500); }); }) .And(and => { and.LessOrEqual(x => x.MemberSince, new DateTime(2000, 1, 1)); and.Greater(x => x.MemberSince, new DateTime(1980, 1, 1)); and.Where(x => x.LastName == "Scott" || x.LastName == "Jones"); }) .Null(x => x.LastName) .NotNull("FirstName"); }; Because of = () => target = criteria.Build(sessionFactory.New()); It should_render_correct_sql = () => target.CommandText.Should().Equal( "((Bar < @0) and (Bar >= @1) and ((FirstName != @2) or ((Foo = @3) and (Baz = @4))) and ((MemberSince <= @5) and (MemberSince > @6) and ((LastName = @7) or (LastName = @8))) and (LastName is NULL) and (FirstName is not NULL))"); It should_contain_expected_parameters = () => { target.Parameters.Should().Count.Exactly(9); target.Parameters.Should().Contain.One(x => x.Name == "@0" && x.Value.Equals(1000)); target.Parameters.Should().Contain.One(x => x.Name == "@1" && x.Value.Equals(300)); target.Parameters.Should().Contain.One(x => x.Name == "@2" && x.Value.Equals("Tim")); target.Parameters.Should().Contain.One(x => x.Name == "@3" && x.Value.Equals(25)); target.Parameters.Should().Contain.One(x => x.Name == "@4" && x.Value.Equals(500)); target.Parameters.Should().Contain.One(x => x.Name == "@5" && x.Value.Equals(new DateTime(2000, 1, 1))); target.Parameters.Should().Contain.One(x => x.Name == "@6" && x.Value.Equals(new DateTime(1980, 1, 1))); target.Parameters.Should().Contain.One(x => x.Name == "@7" && x.Value.Equals("Scott")); target.Parameters.Should().Contain.One(x => x.Name == "@8" && x.Value.Equals("Jones")); }; }
The criteria API is limited in that it only supports conditions on simple properties. It does not support conditions on nested properties or aggregate operations on collections.
Where Criteria leaves off, DbCommandSpec picks up. It allows you to do more complex queries and still return entities instead of raw data. For example:
var command = new DbCommandSpec() .SetCommandText( @"select e.* from TimeEntry e inner join Project p on p.Id = e.ProjectId inner join Client c on c.Id = p.ClientId where c.Id = @clientId") .AddParameter(clientId => id); var timeEntries = UnitOfWork.Current.Session.List<TimeEntry>(command);
In cases where you want to fetch raw data, perhaps to populate DTOs, you can use ExecuteQuery. For example:
var command = new DbCommandSpec() .SetCommandText( @"select e.Id, c.Name from TimeEntry e inner join Project p on e.ProjectId = p.Id inner join Client c on p.ClientId = c.Id where c.Name = @name") .AddParameter("@name", "Acme") var timeEntries = UnitOfWork.Current.Session.ExecuteQuery(command);
timeEntries
is an IEnumerable<IDictionary<string, object>>
, each item of which is a row. Each item in the dictionary is a key-value pair with Key being the projection name and Value being the value.
var command = new DbCommandSpec() .SetCommandText("create table Foo (Bar varchar(200))"); UnitOfWork.Current.Session.ExecuteNonQuery(command);
var command = new DbCommandSpec() .SetCommandText("select count(*) from Foo where Bar = 'bar'"); int count = UnitOfWork.Current.Session.ExecuteScalar<int>(command);
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4