Dapper FluentMap: property with name 'Date' can't be mapped - dapper

I've faced the problem with Dapper FluentMap (version 1.60) which I don't know how to fix. It looks like Dapper can't map a column from a table if the corresponding property in .NET class has "Date" name.
I have the table in DB (I'm using MS SQL if that's matter) with column Dt of Date type.
There is an entity with DateTime property:
public class MyEntity {
public DateTime Date { get; set; }
}
and the corresponding mapping:
public class MyEntityMapper : EntityMap<MyEntity> {
public MyEntityMapper() {
Map(p => p.Date).ToColumn("Dt");
}
}
When I try to get data from the DB and map to MyEntity, I get the following error:
ArgumentNullException: Value cannot be null. Parameter name: meth
If I rename Date property in MyEntity to something else like Dt or JustDate - everything works fine. Is there such restriction in Dapper Fluent Map (not allowed to give a name to property equal to data type name in DB)?
If so, is it possible to overcome it somehow? Because in my case it's a bit problematic to rename property in MyEntity

Yes you are right, the only solution you can do was to change your entity property Date into other name.

Related

Dapper Extension Get & Update returns errors

I tried to play with Dapper Extension & MS Access and succeeded up to certain extent. My code is listed below. All the functions works properly (Insert/Count/GetList/Delete) except Get & Update. I have summarised the code below. If anybody wants I can paste all the code here
My Product class
public class Products
{
public string ProductNumber { get; set; }
public string Description { get; set; }
}
And in my main class. I tried to get the product and update it as below. con.Get<Products> function returns an exception with "Sequence contains more than one element" message and con.Update<Products> returns an exception with "At least one Key column must be defined".
using (var con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb"))
{
string ProductNumber = "12";
var product4 = con.Get<Products>(ProductNumber);
product4.ProductNumber = "Baz";
con.Update<Products>(product4);
Console.ReadLine();
}
Even though con.Get<Products> fails con.GetList<Products>(predicate) works perfectly. I did follow this link for setup
If DapperExtensions can't infer a key property called ID from your class, you'll need to explicitly specify one via a class mapper. Assuming the ProductNumber is the primary key in the database, the following example class mapper should set the ProductNumber to be the primary key for DapperExtensions.
using Dapper;
using DapperExtensions;
using DapperExtensions.Mapper;
public class ProductsMapper : ClassMapper<Products>
{
public ProductsMapper()
{
Map(x => x.ProductNumber).Key(KeyType.Assigned);
AutoMap();
}
}
This class can sit somewhere within the same assembly as the rest of your code. Dapper Extensions will automatically pick it up. If you have your classes and Dapper code in separate assemblies, you can point it to your mapper with the following line:
DapperExtensions.DapperExtensions.SetMappingAssemblies({ typeof(ProductsMapper).Assembly })

Entity Framework 6 Code First VarBinary Length in mapping or computed column

I see it is possible to use a query to find a length of a varbinary field in a query.
Entity framework and VARBINARY
But can you map a FileSize property to get the size of a varbinary in the Fluent API without having to write queries?
I want to be able to store some file information in SQL Server. This data is a filename, file contents. This has a definition in the data model as follows:
public class FileData
{
public int Id { get; set; }
public string Filename { get; set; }
public byte[] Contents { get; set; }
}
Typically, I want to know the size of the contents when I retrieve FileData. If I added an int property FileSize to FileData, can you map this property to compute the size?
I can configure this in SQL:
And when I insert into the table it gives me the correct size.
However, if I generate my data model/DbContext using VS2015 -> Add New Item... -> ADO.NET Entity Data Model, my FileData POCO only has an annotation with DatabaseGenerated(DatabaseGeneratedOption.Computed) on the FileSize property. Nothing else reflects the len(Contents) formula I added.
Is it possible to configure this sort of computed column using EF6 Code First?
You can't create computed columns in entity framework code first. The way you're doing it is correct, you just put the DatabaseGenerated(DatabaseGeneratedOption.Computed) attribute on the property, Entity Framework won't try to create the column then for you, and you create the column manually. A good idea is to make the property private set, since you won't ever need to manually modify it:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public int? FileSize {get; private set;}

Dapper can't ignore nested objects for parameter?

I am beginning to use Dapper and love it so far. However as i venture further into complexity, i have ran into a big issue with it. The fact that you can pass an entire custom object as a parameter is great. However, when i add another custom object a a property, it no longer works as it tries to map the object as a SQL parameter. Is there any way to have it ignore custom objects that are properties of the main object being passed thru? Example below
public class CarMaker
{
public string Name { get; set; }
public Car Mycar { get; set; }
}
propery Name maps fine but property MyCar fails because it is a custom object. I will have to restructure my entire project if Dapper can't handle this which...well blows haha
Dapper extensions has a way to create custom maps, which allows you to ignore properties:
public class MyModelMapper : ClassMapper<MyModel>
{
public MyModelMapper()
{
//use a custom schema
Schema("not_dbo_schema");
//have a custom primary key
Map(x => x.ThePrimaryKey).Key(KeyType.Assigned);
//Use a different name property from database column
Map(x=> x.Foo).Column("Bar");
//Ignore this property entirely
Map(x=> x.SecretDataMan).Ignore();
//optional, map all other columns
AutoMap();
}
}
Here is a link
There is a much simpler solution to this problem.
If the property MyCar is not in the database, and it is probably not, then simple remove the {get;set;} and the "property" becomes a field and is automatically ignored by DapperExtensions. If you are actually storing this information in a database and it is a multi-valued property that is not serialized into a JSON or similar format, I think you are probably asking for complexity that you don't want. There is no sql equivalent of the object "Car", and the properties in your model must map to something that sql recognizes.
UPDATE:
If "Car" is part of a table in your database, then you can read it into the CarMaker object using Dapper's QueryMultiple.
I use it in this fashion:
dynamic reader = dbConnection.QueryMultiple("Request_s", param: new { id = id }, commandType: CommandType.StoredProcedure);
if (reader != null)
{
result = reader.Read<Models.Request>()[0] as Models.Request;
result.reviews = reader.Read<Models.Review>() as IEnumerable<Models.Review>;
}
The Request Class has a field as such:
public IEnumerable<Models.Review> reviews;
The stored procedure looks like this:
ALTER PROCEDURE [dbo].[Request_s]
(
#id int = null
)
AS
BEGIN
SELECT *
FROM [biospecimen].requests as bn
where bn.id=coalesce(#id, bn.id)
order by bn.id desc;
if #id is not null
begin
SELECT
*
FROM [biospecimen].reviews as bn
where bn.request_id = #id;
end
END
In the first read, Dapper ignores the field reviews, and in the second read, Dapper loads the information into the field. If a null set is returned, Dapper will load the field with a null set just like it will load the parent class with null contents.
The second select statement then reads the collection needed to complete the object, and Dapper stores the output as shown.
I have been implementing this in my Repository classes in situations where a target parent class has several child classes that are being displayed at the same time.
This prevents multiple trips to the database.
You can also use this approach when the target class is a child class and you need information about the parent class it is related to.

Dapper: Throw error on failed mapping

Is there an option to have dapper throw an error if the Query method fails to find a property matching a returned column in a result set?
For example:
public class Person{
public String FirstName {get; set;}
public String LastName {get; set;}
}
...
conn.Query<Person>("select FName, LName from Users");
The above would throw no error despite that no data was transferred due to the name mismatch.
If not is there a reason not to add it? My old home-grown micro-ORM did this and I miss the feature so I'd consider attempting to add it, but not if there were specific design decisions that already eliminated it (ie "raw performance").
I ended up using a chunk of code called SafeDapper:
https://github.com/LarrySmith-1437/SafeDapper
Different naming property you must be manual mapping.
Install Dapper.FluentMap NuGet package.
Manual mapping
public class PersonMap : EntityMap<Person>
{
public PersonMap()
{
// Map property 'FirstName' to column 'FName'.
Map(p => p.FirstName)
.ToColumn("FName");
// Map property 'FirstName' to column 'FName'.
Map(p => p.LastName)
.ToColumn("LName");
}
}
Initialization:
FluentMapper.Initialize(config =>
{
config.AddMap(new PersonMap());
});

Entity Framework 7 migration: how to get a varchar column with a length greater than 1?

I have the following:
[Table("Countries")]
public class Country
{
public int CountryId { get; set; }
[MaxLength(255), Required]
public string CountryName { get; set; }
[Column(TypeName = "varchar")]
public string CountryCode { get; set; }
}
Every time I apply my migration, CountryCode becomes a varchar column with a max length of 1. I tried setting the MaxLength annotation to 255 and still get a max length of 1. When the column is set as nvarchar it works as expected. Am I doing something wrong?
Edit: Whenever I explicitly set the string data type, length sets to 1. I could have used Column(TypeName = "nvarchar")] and the length sets as 1..
Would of liked to have used data annotations, but I was able to get it to work with:
builder.Entity<Country>()
.Property(e = e.CountryCode).HasSqlServerColumnType("VARCHAR(30)");
Data Annotations are not yet fully implemented in Entity Framework 7.
You can still add annotations to your entity classes so that they are
used by other frameworks (such as ASP.NET MVC), but Entity Framework
will not process these annotations. You can track support for Data
Annotations on GitHub.
http://ef.readthedocs.org/en/latest/modeling/configuring.html
OK using .net core 2 targeting sql server I had this issue. My model builder looked like this.
modelBuilder.Entity<StreamCheckpointDto>()
.Property(e => e.StreamName)
.HasColumnType("varchar")
.HasMaxLength(40).IsUnicode(false);
Once I removed the HasColumnType and let it choose varchar automatically based on my StreamCheckpointDto.StreamName being a string the length became 40 as expected. you might also be able to use.
.HasColumnType("varchar(40)")
instead.

Resources