Introduction

Most projects tend to be a mixture of basic CRUD calls and more sophisticated queries. The ideal ORM provides an easy to way to automatically generate and execute the bulk of the queries, while also providing an easy to way to tap into the power of writing custom SQL queries for the more demanding parts of the application (without requiring you to become a guru on a third party ORM querying language).

The fact is that most .NET developers are already intimately familiar with creating both Linq and SQL queries.
Marr.DataMapper capitalizes on this by providing two main ways to interact with the database:

1) Using Linq syntax which automatically generates and executes queries on your behalf (including parameter creation)
2) Using manually specified queries when tighter control is required, allowing MDM to behave more like a "micro ORM" (the thinnest possible layer over ADO.NET)

The only thing you have to do to get this functionality is to create your mappings. Mappings can be achieved easily using an intuitive fluent mapping interface. Or if you prefer a more declarative approach, mappings can also be created by simply sprinkling Table, Column and Relationship attributes onto your entity models.

Marr.DataMapper supports one-to-one, one-to-many, and inheritance based models with ease (and no special base class is required).

books.png Learn more: documentation

nuget.png Find us on NuGet: https://nuget.org/packages/MarrDataMapper

github.png Find us on GitHub: http://www.github.com/jordanmarr/marr.datamapper

News

As of 4/3/2013, the source code repository has been migrated from TFS to Git to provide an easier path for users to fork the code and contribute updates. Additionally, MDM is now mirrored on GitHub:
http://www.github.com/jordanmarr/marr.datamapper

Thanks to keivanbeigi, for the suggestion to change to Git, and for copying the repository to GitHub!)

Highlights

  • Fluent Mappings
public void InitMappings()
{ 
    FluentMappings mappings = new FluentMappings(); 

    mappings 
        .Entity<Person>() 
            .Table.MapTable("PersonTable") 
            .Columns.AutoMapSimpleTypeProperties() 
                .PrefixAltNames("person_")
                .For(p => p.ID) 
                    .SetPrimaryKey() 
                    .SetReturnValue() 
                    .SetAutoIncrement() 
            .Relationships.AutoMapICollectionOrComplexProperties() 
        .Entity<Pet>() 
            .Columns.AutoMapSimpleTypeProperties() 
                .PrefixAltNames("pet_")
                .For(p => p.ID) 
                    .SetPrimaryKey() 
                .For(p => p.Name) 
}

  • Lazy loaded relationships defined using expressive Linq syntax:
public void InitMappings()
{ 
    FluentMappings mappings = new FluentMappings(); 

    mappings 
        .Entity<Order>() 
            .Columns.AutoMapSimpleTypeProperties() 
            .Relationships.AutoMapICollectionOrComplexProperties() 
                .For("_orderItems").LazyLoad((db, order) => 
                    db.Query<OrderItem>().Where(oi => oi.OrderID == order.ID).ToList());

        .Entity<OrderItem>() 
            .Columns.AutoMapSimpleTypeProperties() 
}



  • Linq syntax for easy parameterized querying with ORM capabilities:
List<Person> people = db.Query<Person>().Where(p => p.Age >= 18);

  • Custom query builder makes it easy to dynamically append where conditions:
List<Person> people = db.Query<Person>()
                .Where(p => p.Name == "Bob" || p.Name == "Robert")
                .AndWhere("([Age] = 35)")
                .OrderBy(p => p.Name);

// Generates a query with the following where clause: 
"WHERE ([t0].[Name] = @P0 OR [t0].[Name] = @P1) AND ([Age] = 35)"

  • Convenient syntax for querying simple lists of primitive data types
List<string> names = db.Query<string>("SELECT FirstName + ' ' + LastName FROM PersonTbl");

  • Can easily access the DataReader directly if needed:
List<Person> people = db.ExecuteReader(
                "SELECT * FROM tblPerson",
                r => new Person { ID = r.GetInt32(0), Name = r.GetString(1) }
);

  • Can translate unnormalized query data (like a view) into a multilayered object graph by using the .Graph() method, or by using the QueryToGraph method:
List<Person> people = db.Query<Person>()
                .Graph()
                .Where(p => p.Age >= 18);


or
List<Person> people = db.QueryToGraph<Person>("SELECT * FROM Person WHERE Age >= 18");

  • Paging is supported for SQL Server 2005 and higher:
                int pageNumber = 1;
                int pageSize = 20;

                var results = db.Query<Company>()
                        .OrderBy(c => c.Name)
                        .Page(pageNumber, pageSize)
                        .ToList();

or using standard linq paging syntax
                int pageNumber = 1;
                int pageSize = 20;

                var results = db.Query<Company>()
                        .OrderBy(c => c.Name)
                        .Skip(pageNumber - 1)
                        .Take(pageSize)
                        .ToList();

  • Ability to get rowcount
                List<Message> messages = UoW.DB.Query<Message>()
                    .Where(m => m.ToUser == recipient)
                    .OrderByDescending(m => m.SentOn)
                    .Page(pageNumber, pageSize)
                    .ToList();

                int totalRecords = UoW.DB.Query<Message>()
                    .Where(m => m.ToUser == recipient)
                    .GetRowCount();

  • Testable Interface driven design
IDataMapper db = MockRepository.GenerateMock<IDataMapper>();

            var queryBuilder = MockRepository.GenerateMock<QueryBuilder<Product>>();
            var sortBuilder = MockRepository.GenerateMock<SortBuilder<Product>>();
            queryBuilder.Expect(b => b.Where("")).IgnoreArguments().Return(sortBuilder);
            sortBuilder.Expect(b => b.ToList()).Return(new List<Product> {
                new Product { ID = 18, Name = "Product18" },
                new Product { ID = 19, Name = "Product19" },
                new Product { ID = 20, Name = "Product20" }
            });
            

            db.Expect(d => d.Query<Product>()).Return(queryBuilder);

  • Attribute based mapping is available as an alternative to the fluent mapping.
[Table("PersonTable")]
public class Person
{
        [Column("First_Name"]
        public string FirstName { get; set; }

        [Column] // Uses property name by default
        public string Description { get; set; }
}

  • Easy instantiation of the DataMapper:
protected IDataMapper CreateDB()
{
    IDataMapper db = new DataMapper(
            System.Data.SqlClient.SqlClientFactory.Instance, 
            "[db connection string goes here]");

    return db;
}

  • MDM uses a FastReflection library that uses caching in conjunction with reflection to provide very fast results
  • MDM can also work in medium trust environments (ie GoDaddy) using the optional "SimpleReflectionStrategy"

Learn more: documentation

Last edited May 4, 2013 at 12:37 AM by jmarr, version 44