Is there a way with dapper-dot-net to use an attribute to specify column names that should be used and not the property name?
public class Code
{
public int Id { get; set; }
public string Type { get; set; }
// This is called code in the table.
public string Value { get; set; }
public string Description { get; set; }
}
I'd like to be able to name my properties whatever I choose. Our database has no consistent naming convention.
If not with dapper, are there any additional similar options?
You can also check out Dapper-Extensions.
Dapper Extensions is a small library that complements Dapper by adding
basic CRUD operations (Get, Insert, Update, Delete) for your POCOs.
It has an auto class mapper, where you can specify your custom field mappings. For example:
public class CodeCustomMapper : ClassMapper<Code>
{
public CodeCustomMapper()
{
base.Table("Codes");
Map(f => f.Id).Key(KeyType.Identity);
Map(f => f.Type).Column("Type");
Map(f => f.Value).Column("Code");
Map(f => f.Description).Column("Foo");
}
}
Then you just do:
using (SqlConnection cn = new SqlConnection(_connectionString))
{
cn.Open();
var code= new Code{ Type = "Foo", Value = "Bar" };
int id = cn.Insert(code);
cn.Close();
}
Keep in mind that you must keep your custom maps in the same assembly as your POCO classes. The library uses reflection to find custom maps and it only scans one assembly.
Update:
You can now use SetMappingAssemblies to register a list of assemblies to scan:
DapperExtensions.SetMappingAssemblies(new[] { typeof(MyCustomClassMapper).Assembly });
If you are using a select statement directly or in a procedure you can just alias the columns.
SELECT code as Value FROM yourTable
Another approach is to just manually map it with the dynamic result.
var codes = conn.Query<dynamic>(...sql and params here...)
.Select<dynamic,Code>(s=>new Code{Id = s.Id, Type = s.Type, Value = s.code, Description = s.Description});
Clearly this introduces type-safety scenarios because you are querying on dynamic. Also, you have to manually map columns which is a bummer.
However, I tend to like this approach because it's so darned transparent. You can cast if need be (as is the case with Enums), and basically just do whatever it is you need to do to go from the db recordset to your properties.
For selects, you can add constructors to your classes to perform the mapping.
The constructor parameter names must match the table columns.
Below is an example from the source. The table will be correctly mapped to the class.
Table:
CREATE TABLE #Users (Id int, Name varchar(20))
Class:
class UserWithConstructor
{
public UserWithConstructor(int id, string name)
{
Ident = id;
FullName = name;
}
public int Ident { get; set; }
public string FullName { get; set; }
}
Related
I'm trying to perform a simple query and the result data is almost all null.
I have this table structure
Table Registros
ID | Autonumeric
TareaM_Id | Numeric
Fecha | Date/Time
and Macro_tareas table
ID | Autonumeric
Nombre | Short Text
I have mapped the classes in C# like this:
[Table("Registros")]
public class Registro
{
[Column("ID")]
public virtual int ID { get; set; }
[Column("Fecha")]
public virtual DateTime Fecha { get; set; }
[Column("TareaM_Id")]
public virtual int TareaM_Id { get; set; }
public virtual MacroTarea MacroT { get; set; }
}
[Table("Macro_tarea")]
public class MacroTarea
{
[Column("ID")]
public virtual int ID { get; set; }
[Column("Nombre")]
public virtual string Nombre{ get; set; }
public virtual ICollection<Registro> Registros { get; set; }
}
This is the query i'm trying to use
string sql = #"SELECT reg.ID, mac.ID
FROM Registros as reg INNER JOIN Macro_tarea as mac on reg.TareaM_Id = mac.ID
WHERE Fecha = #Fecha";
using (IDbConnection db = new OleDbConnection(ConnectionString))
{
var result = db.Query<Registro,MacroTarea, Registro>(sql,
(reg,mac) =>
{
reg.MacroTarea = mac;
return reg;
}
,new { #Fecha = new DateTime(2019, 1, 4).Date }
, splitOn: "mac.ID")
.AsList();
}
I'm trying to only retrieve ids, but both id become null why is this happening?
The thing is, if I add Registros.Fecha and Macro_tarea.Nombre to the query, it got the value correctly. But id keep coming null.
Apparently the issue is happening only with ids. I suspect this issue is due to duplicate column names.
I'm working with Microsoft Access just in cast that matters.
My question is not similar to the possible duplicate because I have the classes defined as they should be mapped.
Renaming your database columns because your code cannot cope with the data is not a good idea. In the world of separation of concerns, why should your database care? There are good database reasons to name ID columns "Id", and you may not even have the option to change them.
There's another issue with Dapper mapping that renaming columns does not get around; repeated types. If you are trying to map to more than one instance of a class Dapper gets confused, and renaming columns won't work because you will rename both instances.
Here is the solution I have come up with. It's similar to a lot of examples that use a dictionary, except:
it can nest to as many levels as you like
can cope with Dappers 7 item limit
can cope with duplicates of the same class
can be reused e.g., for Get, GetCurrent and GetAll
In this example there is an Auction that has many Lots. Each Lot may have 1 or many Items. Items might be packs of Items. The Items are from a limited catalogue and we like relational data, so a Things table contains the details on each Item, like colour, size, etc. Here we are only getting a single Lot, but getting an Auction is the same with another level on top for Auction.
Parameter 1 - The SQL to get everything in one go
Parameter 2 - A Type array of each object we'll get back. For this reason it's best to order your SELECT to group the fields into the classes
Parameter 3 - Call the method we're about to write with the SQL result
Parameter 4 - Standard parameter array for the SQL. SQL Injection is bad, m'kay?
public async Task<List<Lot>> GetAll(int auctionId)
{
using (var connection = new SqlConnection(_appSettings.ConnectionString))
{
await connection.OpenAsync();
var result = new List<Lot>();
await connection.QueryAsync($#"
SELECT [Lot].*,
[Item].[Id],
[Item].[LotId],
[Item].[Notes],
itemDetails.[Id],
itemDetails.[ThingId],
itemDetails.[Colour],
itemDetails.[Size],
[SubItem].[Id],
[SubItem].[ItemId],
[SubItem].[Notes],
subItemDetails.[Id],
subItemDetails.[ThinId],
subItemDetails.[Colour],
subItemDetails.[Size]
FROM [Lot]
INNER JOIN [Item] ON [Item].[LotId] = [Lot].[Id]
LEFT JOIN [Thing] AS itemDetails ON itemDetails.[Id] = [Item].[ThingId]
LEFT JOIN [SubItem] ON [SubItem].[ItemId] = [Item].[Id]
LEFT JOIN [Thing] AS subItemDetails ON subItemDetails.[Id] = [SubItem].[ThingId]
WHERE [AuctionId] = #{nameof(auctionId)}
ORDER BY [Lot].[Id], [Item].[Id], [Expansion].[Id];",
new Type[] {
typeof(Lot),
typeof(Item),
typeof(Thing),
typeof(Expansion),
typeof(Thing)
},
MapResult(result),
new
{
AuctionId = auctionId
}
);
return result.ToList();
}
}
private Func<object[], Lot> MapResult(List<Lot> result)
{
return (obj) =>
{
Lot lot = (Lot)obj[0];
Item item = (Item)obj[1];
Thing itemDetails = (Thing)obj[2];
SubItem subItem = (SubItem)obj[3];
Thing subItemDetails = (Thing)obj[4];
if (lot != null)
{
if (result.Any(a => a.Id == lot.Id))
{
lot = result.First(a => a.Id == lot.Id);
}
else
{
result.Add(lot);
}
}
if (item != null)
{
if (lot.Items.Any(i => i.Id == item.Id))
{
item = lot.Items.First(i => i.Id == item.Id);
}
else
{
lot.Items.Add(item.FromThing(itemDetails));
}
}
if (expansion != null)
{
if (item.SubItems.Any(e => e.Id == subItem.Id) == false)
{
item.SubItems.Add(subItem.FromThing(subItemDetails));
}
}
return null;
};
}
MapResult is the meat of the code. It returns a Func with two types, the Type array we defined above and the return Type, and takes a List of the top level object.
I then map each item from the object array to another of it's actual type. This keeps the code easier to read, and enables properties and methods of the object to be accessed without issue.
Then it's a case of stepping down the hierarchy, checking at each step if one already exists with a matching id, and swapping the iterator to a reference to it if it does. This means that following code will add to the existing item.
In the particular case I've also added a FromThing function to allow easier combining of object properties.
As we discussed in comments, this is an issue due to duplicate column names in two tables. This is where the similar issue and solution could be found. But, it does not include "mapping by code" as you said. So it is not exact duplicate.
I suggest you change the names of ID fields in your tables to avoid colliding them. Of-course, you should also change the name of your POCO properties and mappings accordingly.
If you cannot change the column names in table, change the POCO property name, and use the column alias in SQL query to match those new property names.
I hope this helps you.
The problem was effectively the name of the properties.
I solved it using Custom Column Mapping to do it i got two possible solutions:
Without extensions
First, we define a Dictionary with the name of the column as key, and the name of the property as value
IDictionary<string, string> columnMaps = new Dictionary<string, string>()
{
{ "Macro_tarea.ID", "ID" },
{ "Registros.ID", "ID" }
};
Then, we define a delegate to obtain the PropertyInfo object of the property to which we intend to assign the alias of the previous dictionary
var mapper = new Func<Type, string, PropertyInfo>((type, columnName) =>
{
if (columnMaps.ContainsKey(columnName))
return type.GetProperty(columnMaps[columnName]);
else
return type.GetProperty(columnName);
});
Now, we define an object that implements the ITypeMap interface using CustomPropertyTypeMap implementation
ITypeMap MacroTareaMapper = new CustomPropertyTypeMap(typeof(Macro_tarea),
(type, columnName) => mapper(type, columnName));
ITypeMap RegistrosMapper = new CustomPropertyTypeMap(typeof(Registros),
(type, columnName) => mapper(type, columnName));
Then we register them
SqlMapper.SetTypeMap(typeof(Macro_tarea), MacroTareaMapper);
SqlMapper.SetTypeMap(typeof(Registros), RegistrosMapper);
Simpler solution with Dapper.FluentMap
It is implemented as follows:
We create a class that inherits from EntityMap<T> and using the Map method we define which column corresponds to each property. For example,
internal class Macro_tareaMap : EntityMap<Macro_tarea>
{
internal Macro_tareaMap()
{
//Mi propiedad ID esta asociada a la columna Macro_tarea.ID
Map(x => x.ID).ToColumn("Macro_tarea.ID");
}
}
Then just register it
FluentMapper.Initialize((config) =>
{
config.AddMap(new Macro_tareaMap());
});
Hope it helps another people!
Source: https://medium.com/dapper-net/custom-columns-mapping-1cd45dfd51d6
All of my DAL functions are using dbContext.Database.SqlQuery to map stored procedure results in business logic objects.
My application became more complicated and I'm looking for a modern, "up to date" way to handle the following situations. I know that I can achieve this using the low-level ADO.NET component like SqlDataReader and map the result manually, but I am sure there is the best way to do so using Entity Framework 6.
To the question: with this command dbContext.Database.SqlQuery<MyClass>, I can not handle:
The stored procedure that returns 2 result sets
Mapping the result set to a complex datatype
Example:
public class Order
{
public Customer customer { get; set; }
public Items[] items { get; set; }
}
Again, I know that I can map it manually or with AutoMapper, but I'm looking for an "up to date" approach based on Entity Framework 6.
Yes, there's a way using Translate.
Adapted from the official documentation:
var cmd = dbContext.Database.Connection.CreateCommand();
cmd.CommandText = "[dbo].[GetAllCustomersAndOrders]";
dbContext.Database.Connection.Open();
// Run the sproc
var reader = cmd.ExecuteReader();
var Customers= ((IObjectContextAdapter)dbContext)
.ObjectContext
.Translate<Customer>(reader, "Customers", MergeOption.AppendOnly);
reader.NextResult();
var Orders = ((IObjectContextAdapter)db)
.ObjectContext
.Translate<Order>(reader, "Orders", MergeOption.AppendOnly);
As far as the problem of mapping
few columns from the result to a 2nd level complex type? for example:
SELECT FirstName, LastName, OrderId FROM Orders I want to map it to:
public class Order { public Customer customer { get; set; } public int
OrderId { get; set; } }
The best would be to use a CustomerId inside your Order table, referencing a Customer table, instead of FirstName/LastName. It would be a good refactoring, normalizing the database. Otherwise you will not have a real mapping between your objects and your database, since your Order object will have a Customer property that doesn't exist in your database. In that case, you will have to create a class, e.g. NormalizedOrder
public class NormalizedOrder {
int OrderId { get; set; };
Customer OrderCustomer { get; set; };
}
And then, after the code above where you retrieve all Orders, do something like
var NormalizedOrders = Orders.Select new Order(){OrderId = e.OrderId, OrderCustomer = new Customer(){FirstName=>e.FirstName,LastName=>e.LastName}};
Am using Entity Framework to run a query on a table. However, i need to get select columns only.
class MyEvent
{
public string Name { get; set; }
public int Id { get; set; }
virtual Stage EventStage { get; set; }
..... more columns .....
}
class Stage
{
public string Name { get; set; }
public string Location { get; set; }
..... more columns .....
}
I can write an IQueryable to return these as
dbContext.MyEvents
.Select(s =>
new {
Name = s.Name,
Id = s.Id,
EventStage = new
{
Name = s.EventStage.Name,
Id = s.EventStage.Id
}
}
)
.ToList();
This works as expected, giving me just those columns am interested in.
Now, I need to construct that 'Select' call dynamically using Expression tree, something like here.
How can I achieve that? Is it feasible to construct an anynomous object, like above, via expressions?
EDIT:
The use case for me is that I have a generic dB context class which takes a list of columns as strings to be fetched. In the past, we were returning all columns, ignoring that input list. So, now I need to dynamically generate the select statement to return only the required subset of columns, which can either be done via anonymous object or a dynamically created DTO.
Thanks
Maybe you can use something like the ToDynamic method from here:
https://gist.github.com/volak/20f453de023ff75edeb8
A possible usecase for this problem:
Let the user select the columns to display and query only those selected columns, so you don't query always the whole entity from the database.
Define a strongly typed object and return that. I would avoid using a dynamic object.
Note: you can't return an anonymous object.
I know that I can run from an opened database connection:
SELECT * FROM SYS.PROCEDURES
Is there a way to do the equivalent with EF 6.1?
You can run raw sql statements directly from the Database Context object.
SomeDatabaseEntities db = new ...();
var items = db.Database.SqlQuery<Something>("SELECT * FROM SYS.PROCEDURES")
You will need to make a class called Something that will map the results. Should be as easy as having the property names match up.
From the documentation of the method:
The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type.
Looks like you can just manually map that table using code-first and treat it like any other entity:
[Table("procedures", Schema = "sys")]
public class Procedure
{
[Column(Order = 0)]
public string name { get; set; }
[Key]
[Column(Order = 1)]
public int object_id { get; set; }
}
public partial class Model1 : DbContext
{
public Model1()
: base("name=Model1")
{
}
public virtual DbSet<Procedure> Procedures { get; set; }
}
using (var context = new Model1())
{
foreach (var p in context.Procedures)
{
Console.WriteLine("{0}: {1}", p.object_id, p.name);
}
}
Since you're using DB-first, you can wrap a regular view around the system one, then map an entity to it:
create view dbo.procs
as
select * from sys.procedures;
Here you've got a traditional view living in dbo, so EF shouldn't have any trouble with it.
I have an aggregate class that will contain a collection of another class, but that class will only ever exist in a collection on that aggregate, so I have no need for an ID on it in my code, or a need for a reference to the aggregate. For example:
public class SalesListing
{
public Guid Id { get; set; }
public ICollection<LocalizedDescription> Descriptions { get; set; }
}
public class LocalizedDescription
{
public string CultureCode { get; set; }
public string Title { get; set; }
}
I'd like to just declare the key for the LocalizedDescription class as a combination of SalesListingId and CultureCode without creating a SalesListingId property or reference back to SalesListing. Any way to do this with EF 5.0 ?
For example, here's an example how I'd imagine such an API would look like if it exists:
modelBuilder.Entity<LocalizedDescription>().BelongsTo<SalesListing>(s => s.Description)
.WithKey((s, ld) => new { s.Id, ld.CultureCode })
No, you will still need to specify an ID
You can define composite keys using the following syntax:
modelBuilder.Entity<LocDesc>().HasKey(ld => new { ld.CultureCode, ld.Title });
That said, I think you will regret not defining an integer primary key. If you ever need to export the contents (such as for an external review or translation), matching records by ID will be faster and less error prone than matching by title. Having the ID doesn't hurt and is easier to include now than to retrofit later.
I think you can specify the FK mapping implicitly by not supplying a specific property for the relation in the mapping definition:
modelBuilder.Entity<SalesListing>().HasMany( e => e.Descriptions ).WithRequired();
Should you need to specify the foreign key property, introduce a SalesListingID property for the value and use the following mapping instead:
modelBuilder.Entity<SalesListing>().HasMany( e => e.Descriptions ).WithRequired()
.HasForeignKey( r => r.SalesListingID );