I have two SQL tables: Teams and Members. Each team contains 3 members, in the database the members' ids are stored.
Sample Database
How could I map the Member objects into the Teams using the Dapper.NET ORM?
public class Team
{
public int? id { get; set; }
public Member MemberA { get; set; }
public Member MemberB { get; set; }
public Member MemberC { get; set; }
}
public class Member
{
public int? id { get; set; }
public string Name { get; set; }
}
public IEnumerable<Team> GetTeams()
{
string sql = "SELECT * FROM Teams t LEFT JOIN Members m ON t.MemberA=m.id AND t.MemberB=m.id AND t.MemberC=m.id";
return m_connection.Query<Team, Member, Member, Member, Team>(sql, (t, m1, m2, m3) =>
{
t.MemberA = m1;
t.MemberB = m2;
t.MemberC = m3;
return t;
}, splitOn: "MemberA,MemberB,MemberC");
}
You need to fix your sql query to have a proper join with the Members Table.
Just change it to
string sql = #"SELECT t.ID, t.MemberA, m1.Id, m1.Name,
t.MemberB, m2.Id, m2.Name,
t.MemberC, m3.Id, m3.Name
FROM Teams t LEFT JOIN Members m1 ON t.MemberA=m1.id
LEFT JOIN Members m2 ON t.MemberB=m2.id
LEFT JOIN Members m3 ON t.MemberC=m3.id";
and your dapper code will work as you expect filling the three Member instance of every single Team retrieved.
Notice that when you use multimapping, you need to place the SplitOn elements in the proper place to have Dapper understand your requirement to create three different Member variables.
Version for MS-Access
string sql = #"SELECT t.ID, t.MemberA, m1.Id, m1.[Name],
t.MemberB, m2.Id, m2.[Name],
t.MemberC, m3.Id, m3.[Name]
FROM (((Teams t LEFT JOIN Members m1 ON t.MemberA=m1.id)
LEFT JOIN Members m2 ON t.MemberB=m2.id)
LEFT JOIN Members m3 ON t.MemberC=m3.id)";
Related
I have been working on an ASP.NET MVC app using Entity Framework. Also, it's my first time developing an ASP.NET MVC app. I have been struggling (close to a month of trying and googling) to write a linq query to display results in jQuery datatable for the below SQL query. It involves various left joins and some columns have null values. It would be great if someone could help me out on this. There are 3 tables as below
Assets
Category
Term
SELECT
Asset.Name AS Name,
Asset.Type AS Type,
Asset.Parent_Asset AS "Parent Asset",
Cat.Category AS Category,
Cat.Parent_Category AS "Parent Category",
T.BUSINESS_TERM AS "Business Term",
T.SHORT_DESCRIPTION AS Description
FROM
(SELECT
CH.DISPLAY AS Name,
CH.TYPE AS Type,
PA.DISPLAY AS Parent_Asset,
CH.CATEGORY_INT_ID
FROM
[Metadata].[dbo].[Asset] CH
LEFT JOIN
[Metadata].[dbo].[Asset] PA ON PA.PARENT_ASSET_ID = CH.ASSET_INT_ID) Asset
LEFT JOIN
(SELECT
CH.DISPLAY AS Category,
PA.DISPLAY AS Parent_Category,
CH.CATEGORY_INT_ID AS Category_Id
FROM
[METADATA].[dbo].[Category] CH
LEFT JOIN
[METADATA].[dbo].[Category] PA ON PA.PARENT_CATEGORY_ID = CH.CATEGORY_INT_ID) Cat ON Asset.CATEGORY_INT_ID = Cat.Category_Id
LEFT JOIN
[Metadata].[dbo].[Term] T ON T.CATEGORY_INT_ID = Cat.Category_Id
Create Stored Procedure in the database where the application is connected
CREATE PROCEDURE sp_GetAsset
AS
BEGIN
SELECT
Asset.Name AS Name,
Asset.Type AS Type,
Asset.Parent_Asset AS ParentAsset,
Cat.Category AS Category,
Cat.Parent_Category AS ParentCategory,
T.BUSINESS_TERM AS BusinessTerm,
T.SHORT_DESCRIPTION AS Description
FROM
(
SELECT
CH.DISPLAY AS Name,
CH.TYPE AS Type,
PA.DISPLAY AS Parent_Asset,
CH.CATEGORY_INT_ID
FROM
[Metadata].[dbo].[Asset] CH
LEFT JOIN
[Metadata].[dbo].[Asset] PA ON PA.PARENT_ASSET_ID = CH.ASSET_INT_ID) Asset
LEFT JOIN
(SELECT
CH.DISPLAY AS Category,
PA.DISPLAY AS Parent_Category,
CH.CATEGORY_INT_ID AS Category_Id
FROM
[METADATA].[dbo].[Category] CH
LEFT JOIN
[METADATA].[dbo].[Category] PA ON PA.PARENT_CATEGORY_ID = CH.CATEGORY_INT_ID) Cat ON Asset.CATEGORY_INT_ID = Cat.Category_Id
LEFT JOIN
[Metadata].[dbo].[Term] T ON T.CATEGORY_INT_ID = Cat.Category_Id
);
END
Create class to get data:
public class TestAsset
{
public string Name { get; set; }
public string Type { get; set; }
public string ParentAsset { get; set; }
public string Category { get; set; }
public string ParentCategory { get; set; }
public string BusinessTerm { get; set; }
public string Description { get; set; }
}
Get data in your Context
public class YourContext : DbContext
{
public List<TestAsset> GetAssets()
{
return this.Query<TestAsset>.FromSql("Exec sp_GetAsset").ToList();
}
}
Use GetAssets Method
using (YourContext context = new YourContext())
{
var list = context.GetAssets();
}
I have two tables LookUpCodes and LookUpValues they are defined as below:
public partial class LookUpCodes
{
public int Id { get; set; }
public int? CodeId { get; set; }
public string Code { get; set; }
public string Description { get; set; }
}
public partial class LookUpValues
{
public int Id { get; set; }
public int CodeId { get; set; }
public string CodeValue { get; set; }
public bool? IsActive { get; set; }
}
Each LookUpCode can have multiple Values associated with it. I want to pass in a code and get associated list of values back.
This is probably a common question as I have seen this everywhere, I am not looking for an answer per se, if someone can just explain how to build the proper query I would be obliged.
Here is what I have done so far:
public IEnumerable<LookUpValues> GetValuesByCode(string cd)
{
var query = from code in _context.LookUpCodes
join values in _context.LookUpValues on code.CodeId equals values.CodeId
where code.Code == cd
select new { LookUpValues = values };
return (IEnumerable<LookUpValues>) query.ToList();
}
You are very close to that you are looking for:
public IEnumerable<LookUpValues> GetValuesByCode(string cd)
{
var query = from code in _context.LookUpCodes
join values in _context.LookUpValues
on code.CodeId equals values.CodeId
where code.Code == cd
select values;
return query;
}
Since you have written the join, I assume that you have understood how it works. However let's revisit it:
from a in listA
join b in listB
on a.commonId equals b.commonId
In the above snippet we join the contents of listA with the contents of listB and we base their join on a commonId property existing in items of both lists. Apparently the pair of a and b that fulfill the join criterion it would form one of the possible many results.
Then the where clause is applied on the results of the join. The joined items that pass thewherefilter is the new result. Even at this point the results is still pairs ofaandb`.
Last you project, using the select keyword each pair of the results to a new object. In your case, for each pair of code and values that passed also the where filter, you return only the values.
I would like to fetch from database complex object using single query. Let's look at the following example:
SELECT TableA.*, TableB.*
FROM TableA
INNER JOIN TableA.B_Id = TableB.Id
and corresponding classes:
public class QueryResult
{
public TableA A { get; set; }
public TableB B { get; set; }
}
public class TableA
{
public int Id { get; set; }
public string SomeContentA { get; set; }
public int B_Id { get; set; }
}
public class TableB
{
public int Id { get; set; }
public int SomeContentB { get; set; }
}
I would like to execute the raw SQL query from above against the database and get collection of QueryResult objects with correctly set A and B properties. So far I tried using SqlQuery method, but I only managed to get collection of QueryResult objects with nulls in A and B properties (apparently returned result set was not correctly binded to properties):
var results = ctx.Database.SqlQuery<QueryResult>(\\example_query).ToList();
Note that:
I shouldn't list manually columns in SELECT statement. TableA and TableB classes and SQL tables are likely to change over time, but those changes will be consistent.
Three queries (one to fetch IDs from TableA and TableB, second to fetch objects from TableA, third for objects from TableB) will hurt performance and I should try avoid it if possible.
I am using Entity Framework 4.3 and SQL Server 2012.
Thanks,
Art
You can still use regular EF constructions by just mapping your classes to their corresponding tables and forcing the join in LINQ-To-Entities:
using(var ctx = new MyDbContext())
{
return ctx.TableA
.Join(ctx.TableB, a=>a.B_Id, b=>b.Id, (a,b)=>
new QueryResult{TableA=a, TableB=b});
}
I think that's the only way, at least up to EF6.
I have following objects:
public class City
{
public int CityId { get; set; }
public string Name { get; set; }
public virtual ICollection<CityTranslation> CityTranslations { get; set; }
}
public class CityTranslation
{
public int CityId { get; set; }
public string LanguageCode { get; set; }
public string Translation { get; set; }
public virtual City City { get; set; }
}
City table contain default language value in Name field other translations are in CityTranslation table.
I am having problem to get value by language from this.
I am trying to execute following:
public virtual IEnumerable<City> GetAllByLanguage(string language)
{
if (language != "en")
{
var data = from c in context.Cities
join t in context.CityTranslations
on c.CityId equals t.CityId
&& t.LanguageCode == language
select c;
}
else
{
return dbSet.ToList();
}
}
But I am gettings compile error.
Operator '&&' cannot be applied to operands of type 'int' and 'bool'
plus some casting errors.
What should I do to get value from translation table?
Others have spotted the issue, but you shouldn't even need the join - EF will handle it:
var data = from t in context.CityTranslations
where t.LanguageCode == language
select t.City;
The second predicate should not be part of the join:
var data = from c in context.Cities
join t in context.CityTranslations
on c.CityId equals t.CityId
where t.LanguageCode == language
For a join on multiple values you need to create composite key using anonymous types so your join statement needs to be something like
on new {t.CityId, t.languageCode} equals new {c.CityId, language}
join diffrent field type in linq
public partial class Product
{
public int ID { get; set; }
public string CategoryID
{
get { return Myclass.increse(CategoryID); }
set { CategoryID = value; }
}
public string Name { get; set; }
}
public partial class ProductCategory
{
public int ID { get; set; }
public string Name { get; set; }
}
var query = (from c in dContext.ProductCategories
join p in dContext.Products
on Myclass.EncodeMD5(c.ID.ToString()) equals p.CategoryID
select new { id = p.ID, cat = p.CategoryID, name = p.Name, cat1 = c.Name }
).ToList();
The field should be converted to string
Then function runs EncodeMD5
error:
LINQ to Entities does not recognize the method 'System.String
EncodeMD5(System.String)' method, and this method cannot be translated
into a store expression.
You cannot call arbitrary .NET methods in LINQ-to-(some database backend) - the entire point of EF (etc) is that it wants to create SQL from your expression - something involving a where clause. It can work with simple properties and operators, and a few methods it knows about and can map into SQL, but it can't perform something it has never heard of (increse, EncodeMD5, etc) how would it know what SQL to write?
With something like MD5, your best bet would be to store the MD5 hash in the underlying table along with the ID. Likewise with the CategoryID's "increse" (whatever that is). So your query would end up working off these pre-calculated values:
on c.IDHash equals p.CategoryIDHash