Dapper multipmap - c#

I have the following in my db with a junction/xref table
I would like to map it to the following object
public class Coin : CoinBase
{
public IEnumerable<CoinAnnouncement> Announcements { get; set; }
public IEnumerable<CoinCategory> Categories { get; set; }
}
I would like to include annoucements in one go as well if possible (not showing in the db image)
Here is my dapper call
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
return await conn.QueryAsync<Coin, CoinCategory, Coin>(
#"SELECT c.CoinId, c.Userid, c.IconPath, c.LastPriceBtc, c.LastUpdatedUtc, c.Name, c.Rank,
c.Symbol, c.LogoPath, c.Description, c.SubReddit, c.TwitterScreenName, c.Website, c.Discord,
c.FacebookPage, c.Telegram
FROM Coins c
INNER JOIN CoinCategoriesCategories coinCat ON coinCat.CoinId = c.CoinId
INNER JOIN CoinCategories cat ON cat.CategoryId = coinCat.CategoryID",
(coin, coinCat) => {
coin.Categories = coinCat; //problem figuring out what this line would look like
return coin;
});
}
I essentially want to just ignore the xref/junction table and directly map the categories to the coin object

Solved it based on https://www.tritac.com/blog/dappernet-by-example
var lookup = new Dictionary<int, Coin>();
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
var result = await conn.QueryAsync<Coin, CoinCategoryCategory, CoinCategory, Coin>(
#"SELECT c.*, coinCat.*, cat.*
FROM Coins c
INNER JOIN CoinCategoriesCategories coinCat ON coinCat.CoinId = c.CoinId
INNER JOIN CoinCategories cat ON cat.CategoryId = coinCat.CategoryID",
(c, cat, coinCat) =>
{
Coin coin;
if (!lookup.TryGetValue(c.CoinId, out coin))
{
lookup.Add(c.CoinId, coin = c);
}
if (coin.Categories == null)
coin.Categories = new List<CoinCategory>();
coin.Categories.Add(new CoinCategory { CategoryId = coinCat.CategoryId, Description = coinCat.Description });
return coin;
}, splitOn: "CategoryId, CoinId");
return lookup.Values;
}

Related

Is this an efficient way of mapping with Dapper?

Im trying to map the list of Products and CategoryPictures properties inside the Category entity.
Then im mapping the Picture object inside the CategoryPictures.
Im not sure if this an effiecient way of using dapper as its my first time working with dapper. I used to work with entityframework.
Im using Dapper instead of EF as i want to improve my sql skills.
Should i use multiple results instead?
public async Task<IEnumerable<Category>> GetCategoriesListAsync()
{
using (var conn = SqlConnection())
{
string sql = #"select c.*, cp.*, pd.*, p.*
from Categories c
inner join CategoryPictures cp on cp.CategoryId = c.Id
inner join Products pd on pd.CategoryId = c.Id
inner join Pictures p on p.Id = cp.PictureId";
//string sqlmulti = $#"select * from {tableName};
// select * from Products";
//List<Category> cat = null;
//List<Product> prod = null;
//using (var lists = conn.QueryMultiple(sqlmulti))
//{
// cat = lists.Read<Category>().ToList();
// prod = lists.Read<Product>().ToList();
//}
var lookup = new List<Category>();
await conn.QueryAsync<Category, CategoryPicture, Product, Picture, Category>(sql,
(c, cp, pd, p) =>
{
if (lookup.FirstOrDefault(x => x.Id == c.Id) == null)
{
lookup.Add(c);
}
c.CategoryPictures.AsList().Add(cp);
c.Products.AsList().Add(pd);
foreach (var item in c.CategoryPictures)
{
item.Picture = p;
}
return null;
});
return lookup;
}
}
I've removed the link between Category and Product where the Product was child of Category.
The problem i was having with the previous code was that the Picture child of CategoryPictures was not populating properly. So i set the Picture object of CategoryPicture before populating CategoryPicture.
public async Task<IEnumerable<Category>> GetAllCategories()
{
using (var conn = SqlConnection())
{
string sql = #"select c.Id, c.Name, c.Description, c.Created, c.LastModified,
cp.Id, cp.CategoryId, cp.PictureId,
p.Id, p.Url
from Categories c
inner join CategoryPictures cp on cp.CategoryId = c.Id
inner join Pictures p on p.Id = cp.PictureId";
var lookup = new Dictionary<int, Category>();
await conn.QueryAsync<Category, CategoryPicture, Picture, Category>(sql,
(c, cp, p) =>
{
if (!lookup.TryGetValue(c.Id, out Category found))
{
lookup.Add(c.Id, found = c);
}
cp.Picture = p;
found.CategoryPictures.AsList().Add(cp);
return null;
});
return lookup.Values.ToList();
}
}

How to do a query from an object with a list of objects in Dapper C#

Suppose I have the following tables:
tb_1: |user_id|user_name|email|age|
tb_2: |item_id|item_name|value|
tb_3: |user_id|item_id|
And I have the models below:
Item:
public class Item {
public string Name {get; set;}
public int Value {get; set;}
}
User:
public class User {
public Guid UserId {get; set;}
public List<Item> Itens {get; set;}
}
I am using the following Query to do the search:
using(var connection = ...)
{
var query1 = "SELECT ... FROM tb_1";
var query2 = "SELECT ... FROM tb_2 JOIN tb_3 ON ... WHERE tb_3.user_id = #UserId";
var users = await connection.QueryAsync<User>(query1);
foreach(var user in users)
{
user.Itens = await connection.QueryAsync<Item>(query2, user.UserId);
}
return users;
}
Is it possible to remove the foreach and use only one query?
PS: The tables are N to N.
I was able to solve the problem. I researched and I found a solution in Dapper documentation from the "one to many" query.
string sql = "SELECT TOP 10 * FROM Orders AS A INNER JOIN OrderDetails AS B ON A.OrderID = B.OrderID;";
using (var connection = new SqlCeConnection("Data Source=SqlCe_W3Schools.sdf"))
{
var orderDictionary = new Dictionary<int, Order>();
var list = connection.Query<Order, OrderDetail, Order>(
sql,
(order, orderDetail) =>
{
Order orderEntry;
if (!orderDictionary.TryGetValue(order.OrderID, out orderEntry))
{
orderEntry = order;
orderEntry.OrderDetails = new List<OrderDetail>();
orderDictionary.Add(orderEntry.OrderID, orderEntry);
}
orderEntry.OrderDetails.Add(orderDetail);
return orderEntry;
},
splitOn: "OrderID")
.Distinct()
.ToList();
Console.WriteLine(list.Count);
FiddleHelper.WriteTable(list);
FiddleHelper.WriteTable(list.First().OrderDetails);
}
Reference: Query Multi-Mapping (One to Many)
I would say "yes, but: rewrite the query to use a join", i.e.
var query = #"
SELECT ... FROM tb_1
SELECT ... FROM tb_2 JOIN tb_3 ON ...
INNER JOIN (SELECT UserId FROM tb_1) x -- your original query here
on tb_3.user_id = x.UserId -- but as a sub-query
";
then use QueryMultiple. This returns two grids, so you'll need to read twice - then group and partition at the call-site.
using (var multi = connection.QueryMultiple(sql, ...))
{
var users = multi.Read<User>().AsList();
var allItems = multi.Read<Item>().AsList();
var usersById = users.ToDictionary(x => x.Id);
foreach(var item in allItems) {
usersById[item.UserId].Items.Add(item);
}
return users;
}

How to sort in LINQ If Join other database

Sort in LINQ
I have 2 database CustomerEntities and BillEntities
I want to get CustomerName from CustomerEntities and sort it but it have no data and I want .ToList() just once time because it slow if used many .ToList()
using (var db1 = new CustomerEntities())
{ using (var db2 = new BillEntities())
{
var CustomerData = db1.Customer.Select(s=> new{s.CustomerCode,s.CustomerName}).ToList();
var BillData = (from t1 in db2.Bill
select new {
BillCode = t1.Billcode,
CustomerCode = t1.Customer,
CustomerName = ""; //have no data
});
}
if(sorting.status==true)
{
BillData= BillData.OrderBy(o=>o.CustomerName); //can't sort because CustomerName have no data
}
var data = BillData .Skip(sorting.start).Take(sorting.length).ToList(); // I want .ToList() just once time because it slow if used many .ToList()
foreach (var b in data)
{
var Customer = CustomerData.FirstOrDefault(f => f.CustomerCode==b.CustomerCode );
if(CustomerName>!=null)
{
r.CustomerName = Customer.CustomerName; //loop add data CustomerName
}
}
}
I have no idea to do it. Help me please
I'm not sure if I understand your code but what about this:
var BillData = (from t1 in db2.Bill
select new {
BillCode = t1.Billcode,
CustomerCode = t1.Customer,
CustomerName = db1.Customer.FirstOrDefault(c => c.CustormerCode == t1.Customer)?.CustomerName
});
Then you have objects in BillData that holds the CustomerName and you can order by that:
BillData.OrderBy(bd => bd.CustomerName);
If you just want to get CustomerName from your customer Db and sort it, this is what i would have used. I used orderByDescending but you can use OrderBy aswell.
public List<Customer> getLogsByCustomerName(string customername)
{
using (var dbentites = new CustomerEntities())
{
var result = (from res in dbentites.Customer.OrderByDescending(_ => _.CustomerName)
where res.CustomerName == customername
select res).ToList();
return result.ToList();
}
}

Joining clause in linq

I've created three table's name: emps, emp_project, emp_location
now i want to select one column from each table, but when im executing join query so far getting this this error:
The type of one of the expressions in the join clause is incorrect.
Type inference failed in the call to 'GroupJoin'.
the query which im executing is:
from e in Emp_info
from p in Emp_projects
join l in Emp_locations
on new { e.User_id , p.Project_id } equals new { l.User_id, l.Project_id } into detail
from l in detail
select new
{
e.Middlename,
p.Project_name,
l.Location
};
query.Dump("Join query");
Don't know which of the clauses is causing the error!
My guess, is that the two anonymous types it is trying to compare aren't the same (also ensure that the properties are the same datatype).
Change
on new { e.User_id , p.Project_id } equals new { l.User_id, l.Project_id } into detail
To
on new { UserId = e.User_id, ProjectId = p.Project_id } equals
new { UserId = l.User_id, ProjectId = l.Project_id } into detail
this works for me
void Main()
{
var Emp_info = new List<Info>
{
new Info {Middlename = "Middlename",User_id = "1"}
};
var Emp_projects = new List<Project>
{
new Project{Project_id = "1",Project_name = "Project"}
};
var Emp_locations = new List<LocationInfo>
{
new LocationInfo{Location = "Location",Project_id="1",User_id = "1"}
};
/* your code */
var query = from e in Emp_info
from p in Emp_projects
join l in Emp_locations
on new { e.User_id , p.Project_id } equals new { l.User_id, l.Project_id } into detail
from l in detail
select new
{
e.Middlename,
p.Project_name,
l.Location
};
query.Dump("Join query");
/* your code */
}
class Info
{
public string User_id;
public string Middlename;
}
class Project
{
public string Project_id;
public string Project_name;
}
class LocationInfo
{
public string User_id;
public string Project_id;
public string Location;
}
So far i've come to this solution , by using more than one join at a time.
don't is it the right practice or not.
var query=
from e in Emp_info
join l in Emp_locations on e.User_id equals l.User_id // first join
join p in Emp_projects on l.Location_id equals p.Project_id // second join
select new
{
Name= e.Middlename,
Project = p.Project_name,
Location = l.Location
};
query.Dump();
finally got the answer.. this is working perfectly as i want
var query=
from e in db.emp_info
from p in db.emp_projects
join l in db.emp_locations
on new { username= e.User_id ,project_id=p.project_id } equals new { username=l.User_id,project_id= l.Project_id } into detail
from l in detail
select new
{
e,p,l
};
foreach (var q in query)
{
Console.WriteLine("{0} has done project like {1} in {2}",q.e.Middlename,q.p.project_name,q.l.location);
}
Console.ReadLine();
}
Console.ReadLine();

Populate list from linq query result

I have the following code, I'm returning results that are associated with the userId, I then want to loop through the results and build a collection and return it back to the UI.
public List<UserEmails> LoadUsersInbox(Int64 userId, int status)
{
List<UserEmails> userEmails = null;
using (var sqlCon = new SqlConnection(Context.ReturnDatabaseConnection()))
{
var emails = sqlCon.Query<UserEmailEntity>(#"SELECT e.EmailId, e.ItemId, u.Username as FromUserName, e.EmailReceived
FROM User_Emails e
INNER JOIN User_Profile u on e.FromUserId = u.UserId
WHERE ToUserId = #userId and EmailStatus = #Status",
new { ToUserId = userId, Status = status }).OrderBy(d => d.EmailReceived);
foreach (var item in emails)
{
// loop through and build and List of UserEmails
}
}
return userEmails;
}
But I can't figure out the syntax for it, can some one help me please.
You can transform the list without going to the foreach loop. You do this with the Select statement. I am not able to verify this (thus untested code), but it ought to work if you make do this:
public List<Int64> LoadUsersInbox(Int64 userId, int status)
{
using (var sqlCon = new SqlConnection(Context.ReturnDatabaseConnection()))
{
return sqlCon.Query<UserEmailEntity>
(#"SELECT e.EmailId, e.ItemId, u.Username as FromUserName, e.EmailReceived
FROM User_Emails e
INNER JOIN User_Profile u on e.FromUserId = u.UserId
WHERE ToUserId = #userId and EmailStatus = #Status",
new { ToUserId = userId, Status = status })
.OrderBy(d => d.EmailReceived)
.Select(usrEmlEnt => usrEmlEnt.EmailId);
}
return new List<Int64>();
}

Categories

Resources