*Can You explain the Split on function in the multimap *
I am Trying to get the data from the Database using Dapper ORM. I have received the following error
System.ArgumentException : When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id
Parameter name: splitOn
public abstract class Domain
{
public Guid Id { get; set; }
}
public abstract class ItemBase : Domain
{
private IList<Image> images = new List<Image>();
public Guid? ParentId { get; set; }
public string Name { get; set; }
public IList<Image> Images { get { return images; } }
}
public class Meal : ItemBase
{
}
public class Item : ItemBase
{
private IList<Meal> meals = new List<Meal>();
public IList<Meal> Meals { get { return meals; } };
}
public class Image : Domain
{
public byte Img { get; set; }
public string Description { get; set; }
}
public class MealImageLink : Domain
{
public Guid ItemId { get; set; }
public Guid ImageId { get; set; }
}
/* search function to take dat from the table */
private List<Meal> SearchMeals(Guid id)
{
var query = #"SELECT meal.[Name],meal.[Description],meal.
[Price],mealImage.[Image] as Img
FROM [MealItems] as meal
LEFT JOIN [MealImageLink] mealImageLink
on meal.Id= mealImageLink.MealItemId
LEFT JOIN [Images] mealImage on
mealImageLink.ImageId=mealImage.Id
WHERE meal.[ParentId]=#Id";
List<Meal> meals = ( _connection.Query<Meal, MealImageLink, Image, Meal>
(query, (meal, mealLink, mealImage) =>
{
meal.Images.Add(mealImage);
return meal;
}, new { #Id = id })).ToList();
return meals;
}
The multi-map feature is really more intended for scenarios like:
select foo.Id, foo.whatever, ...,
bar.Id, bar.something, ...,
blap.Id, blap.yada, ...
from foo ...
inner join bar ...
left outer join blap ...
or the lazier but not uncommon:
select foo.*, bar.*, blap.*
from ...
inner join bar ...
left outer join blap ...
But in both of these cases, there is a clear and obvious way to split the horizontal range into partitions; basically, whenever you see a column called Id, it is the next block. The name Id is configurable for convenience, and can be a delimited list of columns for scenarios where each table has a different primary key name (so User might have UserId, etc).
Your scenario seems quite different to this. It looks like you're currently only selecting 4 columns with no particular way of splitting them apart. I would suggest that in this case, it is easier to populate your model via a different API - in particular, the dynamic API:
var meals = new List<Meal>();
foreach(var row in _connection.Query(sql, new { #Id = id }))
{
string name = row.Name, description = row.Description;
decimal price = row.Price;
// etc
Meal meal = // TODO: build a new Meal object from those pieces
meals.Add(meal);
}
The dynamic API is accessed simply by not specifying any <...>. With that done, columns are accessed by name, with their types implied by what they are being assigned to - hence things like:
decimal price = row.Price;
Note: if you want to consume the row data "inline", then just cast as soon as possible, i.e.
// bad: forces everything to use dynamic for too long
new Meal(row.Name, row.Description, row.Price);
// good: types are nailed down immediately
new Meal((string)row.Name, (string)row.Description, (decimal)row.Price);
Does that help?
Tl;dr: I just don't think multi-mapping is relevant to your query.
Edit: here's my best guess at what you intend to do - it simply isn't a good fit for multi-map:
var meals = new List<Meal>();
foreach (var row in _connection.Query(query, new { #Id = id })) {
meals.Add(new Meal {
Name = (string)row.Name,
Images = {
new Image {
Description = (string)row.Description,
Img = (byte)row.Img
}
}
});
}
return meals;
I'm currently building a website in .NET MVC 4, using Entity Framework to access SQL Server.
The website should have a complex search with multiple choices for the user, create a new search (free search), choose from the last 5 searches (history search), choose from stored search parameters.
What I'm having trouble with is the concept of saving the search parameters/sql string, because it's not sessional/cache based and should be stored somewhere (SQL Server / MongoDB / XML) I'm having the hard time in taking the most optimized path, if it's the SQL way then maybe create an entity that stores the search parameters as entities and afterward converting it into a SQL string for the search, or store it in XML and than serialize it with JSON.
Some fields of the search are not an exact db/entity match and requires summing/converting (like hours that would be calculated into certain time).
I'm more inclined to take out the best of Entity Framework abilities for the cause.
Would like to hear some expert thoughts if possible, Thank you.
Not sure if this is the "most optimized" path, but thought it seemed simple to implement:
//POCO class of item you want to search from database
public class SearchableItem
{
public string Name { get; set; }
public int Age { get; set; }
}
//MVC View Model for search page
public class SearchParamaters
{
public int? MinAge { get; set; }
public int? MaxAge { get; set; }
}
//Storable version for database
public class SavedSearchParameters : SearchParamters
{
public int SavedSearchParametersId { get; set; }
}
//Use SearchParameters from MVC, or SavedSearchParamaters from EF
public IQueryable<SearchableItem> DoSearch(SearchParamaters sp)
{
IQueryable<SearchableItem> query = db.SearchableItems;
if (sp.MinAge.HasValue) query = query.Where(x => x.Age >= sp.MinAge.Value);
if (sp.MaxAge.HasValue) query = query.Where(x => x.Age <= sp.MaxAge.Value);
return query;
}
You could also serialize the SearchParameters class as XML/JSON and save it wherever, then deserialize it and pass it to the DoSearch method as normal, then you wouldn't have to change the DB schema every time you wanted to add search parameters
EDIT: Full example using serialization
\Domain\Person.cs
namespace YourApp.Domain
{
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
\Domain\SavedPersonSearch.cs
namespace YourApp.Domain
{
//Entity object with serialized PersonSearchParameters
public class SavedPersonSearch
{
public int Id { get; set; }
public string Name { get; set; }
public string Parameters { get; set; }
}
}
\Models\PersonSearchParameters.cs
namespace YourApp.Models
{
//MVC View Model for search page
public class PersonSearchParameters
{
public int? MinAge { get; set; }
public int? MaxAge { get; set; }
}
}
\Helpers\SearchProvider.cs
using YourApp.Domain;
using YourApp.Models;
namespace YourApp.Helpers
{
public class SearchProvider
{
private YourAppDbContext _context;
public SearchProvider(YourAppDbContext context)
{
//This example uses the DbContext directly
//but you could use a Unit of Work, repository, or whatever
//design pattern you've decided on
_context = context;
}
public IQueryable<Person> SearchPersons(int savedPersonSearchId)
{
var savedSearch = _context.SavedPersonSearches.Find(savedPersonSearchId);
//deserialize (example assumes Newtonsoft.Json)
var searchParams = JsonConvert.Deserialize<PersonSearchParameters>(savedSearch.Parameters);
return SearchPersons(searchParams);
}
public IQueryable<Person> SearchPersons(PersonSearchParameters sp)
{
IQueryable<Person> query = _context.Persons;
if (sp.MinAge.HasValue) query = query.Where(x => x.Age >= sp.MinAge.Value);
if (sp.MaxAge.HasValue) query = query.Where(x => x.Age <= sp.MaxAge.Value);
return query;
}
public void SavePersonSearch(PersonSearchParameters sp, string name)
{
var savedSearch = new SavedPersonSearch { Name = name };
savedSearch.Parameters = JsonConvert.Serialize(sp);
_context.SavedPersonSearches.Add(savedSearch);
_context.SaveChanges();
}
}
}
\Controllers\PersonController.cs
namespace YourApp.Controllers
{
public class PersonsController : Controller
{
private SearchProvider _provider;
private YourAppDbContext _context;
public PersonsController()
{
_context = new YourAppDbContext();
_provider = new SearchProvider(_context);
}
//Manual search using form
// GET: /Persons/Search?minAge=25&maxAge=30
public ActionResult Search(PersonSearchParameters sp)
{
var results = _provider.SearchPersons(sp);
return View("SearchResults", results);
}
//Saved search
// GET: /Persons/SavedSearch/1
public ActionResult SavedSearch(int id)
{
var results = _provider.SearchPersons(id);
return View("SearchResults", results);
}
[HttpPost]
public ActionResult SaveMySearch(PersonSearchParameters sp, name)
{
_provider.SavePersonSearch(sp, name);
//Show success
return View();
}
}
}
conver your parameters to Base64 string. It would help you to create any hard queries for example, http://www.jobs24.co.uk/SearchResults.aspx?query=djAuMXxQUzoxMHx2MC4x¶ms=cXVlcnlmaWx0ZXI6 decode base64 use this service http://www.opinionatedgeek.com/DotNet/Tools/Base64Decode/default.aspx
you can also take a look on http://aws.amazon.com/cloudsearch/ it may be give you an idea about worh with parameters in your project
Something like this could work:
Store the search parameters in json/xml and persist in DB table.
1. When you want to edit the search parameters (if you even allow this), use the json/xml to pre-fill the selected parameters so user can edit criteria.
2. When user wants to run search, take parameters from json and create/run the query.
OR
Store the search parameters in json/xml and persist in DB table and also create the sql query and store the sql string (after validating parameters)
1. When you want to edit the search parameters (if you even allow this), use the json/xml to pre-fill the selected parameters so user can edit criteria.
2. When user wants to run search, simply take the saved query string and execute it.
I am pretty new to Dapper Rainbow so I may be missing something obvious. Is it possible to specify the table name and if so how?
I have tried the following with no luck.
public class DashboardContext : Database<DashboardContext>
{
public DashboardContext()
{
this.DashboardResults = new Table<DashboardResult>(this, "Monitor.DashboardResult");
}
public Table<DashboardResult> DashboardResults { get; set; }
}
I had the same problem but it seems an error in the code. I´ve just commented the lines where is setting the constructor for tables (Database.cs) and it works.
internal void InitDatabase(DbConnection connection, int commandTimeout)
{
this.connection = connection;
//this.commandTimeout = commandTimeout;
//if (tableConstructor == null)
//{
// tableConstructor = CreateTableConstructorForTable();
//}
//tableConstructor(this as TDatabase);
}
I guess this is not the best solution...
You need to hack the rainbow source to get it to work.
Find the CreateTableConstructor method in the file of DataBase.cs.
Just add some code as following:
...
var setters = GetType().GetProperties()
.Where(p => p.GetValue(this, null) == null
&& p.PropertyType.IsGenericType
&& p.PropertyType.GetGenericTypeDefinition() == tableType)
.Select...
For anyone else stumpeling over this post like I did this is now fixed in Dapper.Rainbow version 0.1.3.
It is still in beta at this time (0.1.3-beta1) so if you want to use schema you can clone/fork the repository and run the build script. The binary output can then be used directly or packaged.
As for the table setup you need to define the table name with the name of the schema for for that particular table so for example looking at this example without schema
public class MyDatabase : Database<MyDatabase>
{
public Table<Order> Order{ get; set; }
public Table<Customer> Customer { get; set; }
public Table<Item> Item { get; set; }
}
Which works if you are only using dbo. but if you are for instance using say Product schema for Item you would have to define it using a constructor
public class MyDatabase : Database<MyDatabase>
{
public Table<Order> Order{ get; set; }
public Table<Customer> Customer{ get; set; }
public Table<Item> Item;
public MyDatabase()
{
Item = new Table<Item>(this, "Product.Item");
}
}
The rest should be as before
using (var connection = DbConnections.Create())
{
connection.Open();
var db = MyDatabase.Init((DbConnection)connection, commandTimeout: 2);
var insert = db.Customer.Insert(
// .
//..... your object
// .
);
var insertId = insert.Value;
}
Based on #Acorax answer, it wasn't enough for me, I needed to add brackets to the schema and table name to solve this.
So this solved me the schema problem:
public class MyDatabase : Database<MyDatabase>
{
public Table<Item> Items;
public HamenasDbSchema()
{
Items = new Table<User>(this, "[Schema].[Items]");
}
}
I have a simple class like this
[ActiveRecord("Subscriptions")]
public class Subscription : ActiveRecordBase<Subscription>
{
public Subscription()
{
}
public Subscription(string name)
{
this.Name = name;
}
[PrimaryKey(PrimaryKeyType.Native)]
private int Id { get; set; }
[Property]
public string Name { get; set; }
}
And I want to do a simple lookup using the FindOne() method that's inheritd from the base class. It looks like it uses NHibernate.Criterion.DetachedCriteria but I can't find any examples.
You can also use LINQ, I find the LINQ syntax much more readable:
// find the subscription with id = 3
var subscription = Castle.ActiveRecord.Framework.ActiveRecordLinqBase<Subscription>.Queryable.SingleOrDefault(s => s.Id == 3);
// find all the active subscriptions
var activeSubscriptions = ActiveRecordLinqBase<Subscription>.Queryable.Where(s => s.IsActive);
If your class inherits from ActiveRecordLinqBase you can simply write:
// find the subscription with id = 3
var subscription = Subscription.Queryable.SingleOrDefault(s => s.Id == 3);
// find all the active subscriptions
var activeSubscriptions = Subscription.Queryable.Where(s => s.IsActive);
it isn't very complex:
Subscription.FindOne(NHibernate.Criterion.Expression.Eq("Id",3))
With the Expression-class you have all the things you need to build your sql/hql/select, you just have to nest it again and again.
Subscription.FindOne(Expression.And(Expression.Eq(...),Expression.Eq(...)))
Greetings
Juy Juka
I am new to the Dapper micro ORM. So far I am able to use it for simple ORM related stuff but I am not able to map the database column names with the class properties.
For example, I have the following database table:
Table Name: Person
person_id int
first_name varchar(50)
last_name varchar(50)
and I have a class called Person:
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Please note that my column names in the table are different from the property name of the class to which I am trying to map the data which I got from the query result.
var sql = #"select top 1 PersonId,FirstName,LastName from Person";
using (var conn = ConnectionFactory.GetConnection())
{
var person = conn.Query<Person>(sql).ToList();
return person;
}
The above code won't work as the column names don't match the object's (Person) properties. In this scenario, is there anything i can do in Dapper to manually map (e.g person_id => PersonId) the column names with object properties?
Dapper now supports custom column to property mappers. It does so through the ITypeMap interface. A CustomPropertyTypeMap class is provided by Dapper that can do most of this work. For example:
Dapper.SqlMapper.SetTypeMap(
typeof(TModel),
new CustomPropertyTypeMap(
typeof(TModel),
(type, columnName) =>
type.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false)
.OfType<ColumnAttribute>()
.Any(attr => attr.Name == columnName))));
And the model:
public class TModel {
[Column(Name="my_property")]
public int MyProperty { get; set; }
}
It's important to note that the implementation of CustomPropertyTypeMap requires that the attribute exist and match one of the column names or the property won't be mapped. The DefaultTypeMap class provides the standard functionality and can be leveraged to change this behavior:
public class FallbackTypeMapper : SqlMapper.ITypeMap
{
private readonly IEnumerable<SqlMapper.ITypeMap> _mappers;
public FallbackTypeMapper(IEnumerable<SqlMapper.ITypeMap> mappers)
{
_mappers = mappers;
}
public SqlMapper.IMemberMap GetMember(string columnName)
{
foreach (var mapper in _mappers)
{
try
{
var result = mapper.GetMember(columnName);
if (result != null)
{
return result;
}
}
catch (NotImplementedException nix)
{
// the CustomPropertyTypeMap only supports a no-args
// constructor and throws a not implemented exception.
// to work around that, catch and ignore.
}
}
return null;
}
// implement other interface methods similarly
// required sometime after version 1.13 of dapper
public ConstructorInfo FindExplicitConstructor()
{
return _mappers
.Select(mapper => mapper.FindExplicitConstructor())
.FirstOrDefault(result => result != null);
}
}
And with that in place, it becomes easy to create a custom type mapper that will automatically use the attributes if they're present but will otherwise fall back to standard behavior:
public class ColumnAttributeTypeMapper<T> : FallbackTypeMapper
{
public ColumnAttributeTypeMapper()
: base(new SqlMapper.ITypeMap[]
{
new CustomPropertyTypeMap(
typeof(T),
(type, columnName) =>
type.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false)
.OfType<ColumnAttribute>()
.Any(attr => attr.Name == columnName)
)
),
new DefaultTypeMap(typeof(T))
})
{
}
}
That means we can now easily support types that require map using attributes:
Dapper.SqlMapper.SetTypeMap(
typeof(MyModel),
new ColumnAttributeTypeMapper<MyModel>());
Here's a Gist to the full source code.
This works fine:
var sql = #"select top 1 person_id PersonId, first_name FirstName, last_name LastName from Person";
using (var conn = ConnectionFactory.GetConnection())
{
var person = conn.Query<Person>(sql).ToList();
return person;
}
Dapper has no facility that allows you to specify a Column Attribute, I am not against adding support for it, providing we do not pull in the dependency.
For some time, the following should work:
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
I do the following using dynamic and LINQ:
var sql = #"select top 1 person_id, first_name, last_name from Person";
using (var conn = ConnectionFactory.GetConnection())
{
List<Person> person = conn.Query<dynamic>(sql)
.Select(item => new Person()
{
PersonId = item.person_id,
FirstName = item.first_name,
LastName = item.last_name
}
.ToList();
return person;
}
Here is a simple solution that doesn't require attributes allowing you to keep infrastructure code out of your POCOs.
This is a class to deal with the mappings. A dictionary would work if you mapped all the columns, but this class allows you to specify just the differences. In addition, it includes reverse maps so you can get the field from the column and the column from the field, which can be useful when doing things such as generating sql statements.
public class ColumnMap
{
private readonly Dictionary<string, string> forward = new Dictionary<string, string>();
private readonly Dictionary<string, string> reverse = new Dictionary<string, string>();
public void Add(string t1, string t2)
{
forward.Add(t1, t2);
reverse.Add(t2, t1);
}
public string this[string index]
{
get
{
// Check for a custom column map.
if (forward.ContainsKey(index))
return forward[index];
if (reverse.ContainsKey(index))
return reverse[index];
// If no custom mapping exists, return the value passed in.
return index;
}
}
}
Setup the ColumnMap object and tell Dapper to use the mapping.
var columnMap = new ColumnMap();
columnMap.Add("Field1", "Column1");
columnMap.Add("Field2", "Column2");
columnMap.Add("Field3", "Column3");
SqlMapper.SetTypeMap(typeof (MyClass), new CustomPropertyTypeMap(typeof (MyClass), (type, columnName) => type.GetProperty(columnMap[columnName])));
An easy way to achieve this is to just use aliases on the columns in your query.
If your database column is PERSON_ID and your object's property is ID, you can just do
select PERSON_ID as Id ...
in your query and Dapper will pick it up as expected.
Taken from the Dapper Tests which is currently on Dapper 1.42.
// custom mapping
var map = new CustomPropertyTypeMap(typeof(TypeWithMapping),
(type, columnName) => type.GetProperties().FirstOrDefault(prop => GetDescriptionFromAttribute(prop) == columnName));
Dapper.SqlMapper.SetTypeMap(typeof(TypeWithMapping), map);
Helper class to get name off the Description attribute (I personally have used Column like #kalebs example)
static string GetDescriptionFromAttribute(MemberInfo member)
{
if (member == null) return null;
var attrib = (DescriptionAttribute)Attribute.GetCustomAttribute(member, typeof(DescriptionAttribute), false);
return attrib == null ? null : attrib.Description;
}
Class
public class TypeWithMapping
{
[Description("B")]
public string A { get; set; }
[Description("A")]
public string B { get; set; }
}
Before you open the connection to your database, execute this piece of code for each of your poco classes:
// Section
SqlMapper.SetTypeMap(typeof(Section), new CustomPropertyTypeMap(
typeof(Section), (type, columnName) => type.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false).OfType<ColumnAttribute>().Any(attr => attr.Name == columnName))));
Then add the data annotations to your poco classes like this:
public class Section
{
[Column("db_column_name1")] // Side note: if you create aliases, then they would match this.
public int Id { get; set; }
[Column("db_column_name2")]
public string Title { get; set; }
}
After that, you are all set. Just make a query call, something like:
using (var sqlConnection = new SqlConnection("your_connection_string"))
{
var sqlStatement = "SELECT " +
"db_column_name1, " +
"db_column_name2 " +
"FROM your_table";
return sqlConnection.Query<Section>(sqlStatement).AsList();
}
Messing with mapping is borderline moving into real ORM land. Instead of fighting with it and keeping Dapper in its true simple (fast) form, just modify your SQL slightly like so:
var sql = #"select top 1 person_id as PersonId,FirstName,LastName from Person";
If you're using .NET 4.5.1 or higher checkout Dapper.FluentColumnMapping for mapping the LINQ style. It lets you fully separate the db mapping from your model (no need for annotations)
This is piggy backing off of other answers. It's just a thought I had for managing the query strings.
Person.cs
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public static string Select()
{
return $"select top 1 person_id {nameof(PersonId)}, first_name {nameof(FirstName)}, last_name {nameof(LastName)}from Person";
}
}
API Method
using (var conn = ConnectionFactory.GetConnection())
{
var person = conn.Query<Person>(Person.Select()).ToList();
return person;
}
The simple solution to the problem Kaleb is trying to solve is just to accept the property name if the column attribute doesn't exist:
Dapper.SqlMapper.SetTypeMap(
typeof(T),
new Dapper.CustomPropertyTypeMap(
typeof(T),
(type, columnName) =>
type.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false)
.OfType<ColumnAttribute>()
.Any(attr => attr.Name == columnName) || prop.Name == columnName)));
The easier way (same as #Matt M's answer but corrected and added fallback to default map)
// override TypeMapProvider to return custom map for every requested type
Dapper.SqlMapper.TypeMapProvider = type =>
{
// create fallback default type map
var fallback = new DefaultTypeMap(type);
return new CustomPropertyTypeMap(type, (t, column) =>
{
var property = t.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(typeof(ColumnAttribute))
.Cast<ColumnAttribute>()
.Any(attr => attr.Name == column));
// if no property matched - fall back to default type map
if (property == null)
{
property = fallback.GetMember(column)?.Property;
}
return property;
});
};
for all of you who use Dapper 1.12, Here's what you need to do to get this done:
Add a new column attribute class:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property]
public class ColumnAttribute : Attribute
{
public string Name { get; set; }
public ColumnAttribute(string name)
{
this.Name = name;
}
}
Search for this line:
map = new DefaultTypeMap(type);
and comment it out.
Write this instead:
map = new CustomPropertyTypeMap(type, (t, columnName) =>
{
PropertyInfo pi = t.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false)
.OfType<ColumnAttribute>()
.Any(attr => attr.Name == columnName));
return pi != null ? pi : t.GetProperties().FirstOrDefault(prop => prop.Name == columnName);
});
I know this is a relatively old thread, but I thought I'd throw what I did out there.
I wanted attribute-mapping to work globally. Either you match the property name (aka default) or you match a column attribute on the class property. I also didn't want to have to set this up for every single class I was mapping to. As such, I created a DapperStart class that I invoke on app start:
public static class DapperStart
{
public static void Bootstrap()
{
Dapper.SqlMapper.TypeMapProvider = type =>
{
return new CustomPropertyTypeMap(typeof(CreateChatRequestResponse),
(t, columnName) => t.GetProperties().FirstOrDefault(prop =>
{
return prop.Name == columnName || prop.GetCustomAttributes(false).OfType<ColumnAttribute>()
.Any(attr => attr.Name == columnName);
}
));
};
}
}
Pretty simple. Not sure what issues I'll run into yet as I just wrote this, but it works.
Kaleb Pederson's solution worked for me. I updated the ColumnAttributeTypeMapper to allow a custom attribute (had requirement for two different mappings on same domain object) and updated properties to allow private setters in cases where a field needed to be derived and the types differed.
public class ColumnAttributeTypeMapper<T,A> : FallbackTypeMapper where A : ColumnAttribute
{
public ColumnAttributeTypeMapper()
: base(new SqlMapper.ITypeMap[]
{
new CustomPropertyTypeMap(
typeof(T),
(type, columnName) =>
type.GetProperties( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(prop =>
prop.GetCustomAttributes(true)
.OfType<A>()
.Any(attr => attr.Name == columnName)
)
),
new DefaultTypeMap(typeof(T))
})
{
//
}
}