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");
Related
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();
I have an IQueryable collection of Employee objects which has FirstName, LastName, Department in it. I'm passing a string of LastName separated by comma. I want to use where clause to filter data which has LastName selected as "Sharma,Gupta". Can someone help me out?
Employee class
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Department { get; set; }
public string EmpID { get; set; }
}
public IQueryable<Employee> GetEmpData(String filterExpression)
{
IQueryable<Employee> data = GetEmployeeData();
data = from da in data
where (da.LastName == "Sharma")
select da;
return data;
}
In the above method I can query a single value. filterExpression contains list of LastName separated by a comma. Can someone guide me how to use filterExpression in where clause?
Split your string and use .Contains:
names = filterExpression.Split(",");
IQueryable<Employee> data = GetEmployeeData();
data = from da in data
where names.Contains(da.LastName)
select da;
As you return the entire object and do not project only parts of it using the method syntax might be more readable:
return GetEmployeeData().Where(item => names.Contains(item.LastName));
If your filterExpression is a string, with the names separated by commas, then you'd want to change your query to check if the last name is in the list of names in filterExpression like this:
public IQueryable<Employee> GetEmpData(String filterExpression)
{
List<string> names = filterExpression.Split(",");
return GetEmployeeData().Where(names.Contains(da.LastName));
}
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);
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´).
I am using the following query. It returns 5 rows in a list but all the elements in the list are null.
public class TempClass
{
public int SID { get; set; }
public string SNAME { get; set; }
public string SAGE { get; set; }
}
var i = _dbContext.Database
.SqlQuery<TempClass>("select ID, NAME, AGE from eis_hierarchy")
.ToList();
What I am doing wrong?
Try making the columns returned in the SQL query match the property names in the class you're trying to return:
var i = _dbContext.Database
.SqlQuery<TempClass>("select ID as SID, NAME as SNAME, AGE as SAGE from eis_hierarchy")
.ToList();