Dapper query result sets Pk of table as null in objects - c#

I am trying to retrieve data from mysql database using dapper but the result sets id (primary key) and foreign key as nulls. Other attributes have values.
I tried to change sql query from select * from courses to full form as select id,name,did from courses.
Course{
public Course()
{
}
public string Id { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public bool Is_Elective { get; set; }
public string DId { get; set; }
public int Sem { get; set; }
}
class CourseDAO
{
private readonly MySqlConnection conn;
private string connectionString = "Server=localhost;Database=university;Uid=root;Pwd=*****;";
public CourseDAO()
{
conn = new MySqlConnection(connectionString);
}
public List<Course> getAll()
{
string sql = "select * from university.course";
List<Course> courses = conn.Query<Course>(#sql).ToList();
return courses;
}
}
Expected:
Courses list have all courses from db with correct values.
Actual
Courses list has all courses from db with id and did as null and rest have values.

Even if issue was solved in question comments by Maxim, I'd like to describe problem with few solution alternatives.
Problem cause:
Dapper executes mapping from sql query result to object by name. Sql query result field 'title' is automatically mapped to Course.Title (mapping is case-insensitive).
In your case there was two name-mismatches between db columns vs. C# properties: (course_id != Id and department_id != DId), therefore Dapper was unable to map those fields.
Solution 1, sql column aliases
You can list table columns with possible columns aliases in sql query following way:
string sql = "select course_id Ad Id, title, credits, Is_elective, department_id as DId, sem from university.course";
Using explicit column names in sql, Dapper can execute automatic name-based mappings.
Solution 2, Dapper custom mappings
Dapper Custom mapping is the feature to manually define, for each object, which column is mapped to which property.
Here is class which deal with the mappings (idea for this both-ways mapping borrowed from another SO answer):
public class ColumnMap
{
private readonly Dictionary<string, string> mappings = new Dictionary<string, string>();
public void Add(string t1, string t2)
{
mappings.Add(t1, t2);
}
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("Id", "course_id");
columnMap.Add("DId", "department_id");
SqlMapper.SetTypeMap(typeof (Course), new CustomPropertyTypeMap(typeof (Course), (type, columnName) => type.GetProperty(columnMap[columnName])));
Solution 3, dynamic type and LINQ
You can execute field mapping using dynamic object as following:
string sql = "select * from university.course";
List<Course> courses = conn.Query<dynamic>(#sql)
.Select(item => new Course()
{
Id = item.course_id,
Title = item.title,
Credits = item.credits,
Is_Elective = item.Is_elective,
DId = department_id,
Sem = sem
})
.ToList();

Related

Return NotMapped property in LINQ

I have one entity which is used in Entity Framework Code First.
public class MyEntity
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public List<string> ListOfValues
{
get
{
return StringsAsStrings?.Split(';').ToList();
}
set
{
StringsAsStrings = value.Count > 0 ? String.Join(";", value) : null;
}
}
[Column("ListOfValues")]
private string StringsAsStrings { get; set; }
public int RelationId { get; set; }
}
Here I am giving values as List to public and that should be stored as semicolon(;) separated string as in the table.
As this is specific to this data model, I specified StringsAsStrings as private and created a Convention to include in the migraion. All these are fine.
Now my scenario is to select the collection using LINQ. Please check the following linq query i used;
var items = from A in MyContext.MyEntities.Where(x => x.RelationId == paramId)
select new
{
Id = A.Id,
Name = A.Name,
ListOfValues = A.ListOfValues,
};
var result = items.ToList();
Here I got following error
The specified type member 'ListOfValues' is not supported in LINQ to
Entities. Only initializers, entity members, and entity navigation
properties are supported.
Ok fine. EF didn't support NotMapped properties. So for the work around i just changed private StringsAsStrings to public StringsAsStrings. Then changed the linq like below;
var items = from A in MyContext.MyEntities.Where(x => x.RelationId== paramId)
select new
{
Id = A.Id,
Name = A.Name,
ListOfValues = A.StringsAsStrings.Split(';').ToList(),
};
var s = items.ToList();
Now i got the following error;
LINQ to Entities does not recognize the method 'System.String[]
Split(Char[])' method, and this method cannot be translated into a
store expression.
Please help to solve this case.
Also help me if any other work around for this to avoid changing private to public property.

Shorthand Insert with Dapper - without specifying columns

Is there a way to do a shorthand insert with dapper, without specifying all of the columns? I just want to pass in either a single object or a list of objects. All of my model names match my table names in the db.
I am creating a function that copies one entity from another and don't want to specify columns because code management will be minimal if adding another field in the future.
i.e.
StringBuilder sql = new StringBuilder();
sql.AppendLine("SELECT *");
sql.AppendLine("FROM Product ");
sql.AppendLine("WHERE Id = #Id");
Product source = connection.Query<Product>(sqlCopy.ToString(),
new
{
Id = productId
}, transaction).SingleOrDefault();
// INSERT source entity here without specifying INSERT INTO (COLUMNS)
Have you tried using Dapper.SimplerCRUD (https://github.com/ericdc1/Dapper.SimpleCRUD) or Dapper.Contrib (https://github.com/StackExchange/Dapper/tree/master/Dapper.Contrib)?
Insert Dapper.SimplerCRUD (from github example):
public static int Insert(this IDbConnection connection, object entityToInsert)
Example usage:
[Table("Users")]
public class User
{
[Key]
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
//Additional properties not in database
[Editable(false)]
public string FullName { get { return string.Format("{0} {1}", FirstName, LastName); } }
public List<User> Friends { get; set; }
[ReadOnly(true)]
public DateTime CreatedDate { get; set; }
}
var newId = connection.Insert(new User { FirstName = "User", LastName = "Person", Age = 10 });
Contrib (from github example):
Insert methods
Insert one entity
connection.Insert(new Car { Name = "Volvo" });
or a list of entities.
connection.Insert(cars);

How to map many to many relation to a list with dapper

I have a class AnalysisRule
public class AnalysisRule
{
public long Id { get; set; }
public Analysis Analysis { get; set; }
public AnalysisCategory AnalysisCategory { get; set; }
public Gender Gender { get; set; }
public bool FatherHerdBookRequired { get; set; }
public bool MotherHerdBookRequired { get; set; }
public List<Breed> AllowedBreeds { get; set; }
}
That has a list of Breeds
public class Breed
{
public long BreedId { get; set; }
public long AnimalTypeId { get; set; }
public long BreedCode { get; set; }
public string BreedName { get; set; }
public string BreedAcronym { get; set; }
}
This is a many to many relationship that I bind together with a DB table
AnalysisRulesBreeds
Breeds
And AnalysisRules
With Dapper I have tried
var sql = #"select *
from ""AnalysisRules""
join ""AnalysisCategory"" on ""AnalysisRules"".""AnalysisCategoryId"" = ""AnalysisCategory"".""Id""
join ""Analysis"" on ""AnalysisRules"".""AnalysisId"" = ""Analysis"".""Id""
left join ""AnalysisRulesBreeds"" on ""AnalysisRulesBreeds"".""AnalysisRuleId"" = ""AnalysisRules"".""Id""
left join ""Breed"" on ""AnalysisRulesBreeds"".""BreedId"" = ""Breed"".""BreedId""
where ""AnalysisId"" = :AnalysisId";
rules = sqlConnection.QueryAsync<AnalysisRule, AnalysisCategory, Analysis, Breed, AnalysisRule>(
sql,
(ar, c, a, b) =>
{
ar.AnalysisCategory = c;
ar.Analysis = a;
ar.Breeds.Add(b);
return ar;
},
new
{
AnalysisId = analysisId
},
splitOn:"BreedId");
Which gives me
´When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id
Parameter name: splitOn
If I run the same query in SQL Developer I get 2 rows out with same Id but with different data in Breed, so the query should be good enough.
So how do I get these 2 rows into one AnalysisRule entity where Breeds consist of 2 Breed entities?
EDIT
I now have
sqlConnection.Open();
var sql = #"select ar.*,
ac.*,
b.*
from ""AnalysisRules"" ar
join ""AnalysisCategory"" ac on ar.""AnalysisCategoryId"" = ac.""Id""
join ""Analysis"" a on ar.""AnalysisId"" = a.""Id""
left join ""AnalysisRulesBreeds"" on ""AnalysisRulesBreeds"".""AnalysisRuleId"" = ar.""Id""
left join ""Breed"" b on ""AnalysisRulesBreeds"".""BreedId"" = b.""Id""
where ""AnalysisId"" = :AnalysisId";
var rules = sqlConnection.QueryAsync<AnalysisRule, AnalysisCategory, Analysis, Breed, AnalysisRule>(
sql,
(ar, c, a, b) =>
{
ar.AnalysisCategory = c;
ar.Analysis = a;
ar.Breeds.Add(b);
return ar;
},
new
{
AnalysisId = analysisId
});
return await rules;
Removed the splitOn, changed AnalysisRulesBreedsId to Id but I still get
When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id
Parameter name: splitOn
If I do the same query in SQLDev I get
By selecting * you get the columns of every joined table. Also you set splitOnto BreedId. Now Dapper expects that to separate the row columns of one joined table from the next, it should look for a column named BreedId.
This does not work because all tables except AnalysisRulesBreeds use Id as id column name.
Try removing the splitOn parameter, then it will default to Id. Then adjust your select-clause to only select from the tables you actually need in the result, eg.
select AnalysisRule.*, AnalysisCategory.*, Analysis.*, Breed.*
(assuming that your Analysis table and AnalysisCategory table follow the convention of having an Id column named ´Id´).

Entity framework raw SQL Query

I have to select multiple columns from a database and I don't have a matching entity.
so my query looks like this:
var result = _dbContext.Database.SqlQuery<List<string>>(
"select ID, NAME, DB_FIELD from eis_hierarchy");
I am getting the result set, each row contains list of strings but count is 0.
So how do I select multiple columns using Database.SqlQuery?
You have to capture the results into a class with matching property names, and (at least) a parameterless constructor:
class DbResult
{
public int ID { get; set; }
public string NAME { get; set; }
public string DB_FIELD { get; set; }
}
var result = _dbContext.Database.SqlQuery<DbResult>(
"select ID, NAME, DB_FIELD from eis_hierarchy");

Dapper : Mapper Query not getting values where as Dynamic Object Mapper Query does

I am a beginner to dapper . I was going through the code and building samples . But I am having problems in retrieving data .
My code is as follows
Console.WriteLine("Reading Values");
string readSatement = "select * from employee where Id=#Id ";
IEnumerable<Employee> objEmp1 = con.Query<Employee>(readSatement,
new {
Id = empId
});
var objEmp2 = con.Query(readSatement, new { Id = empId });
In this code objEmp2 retrieves values from db for the id passed . But objEmp1 gives null values for the attributes of the object .
Employee class is as below
public class Employee
{
public int EmpId { get; set; }
public string EmpName { get; set; }
public int EmpAge { get; set; }
}
Whats wrong with the code .
You need to ensure all your database columns either match the properties in your class you are using for the query or you return the columns with names that match. For example in your query above, I believe you might want to write it like:
select Id as EmpId, otherColumn as Propertyname, etc.. from employee
where Id = #Id

Categories

Resources