dapper join splitOn multi column key - dapper

i'm working in new company with huge db...
i'm trying to introduce dapper as orm...
i've got a relation 1-N between two tables, with multi PK
my query is something like
SELECT *
FROM testmag INNER JOIN movmag ON
movmag.code= testmag.code AND
tm_type = mm_type
AND tm_year = mm_year
AND tm_serie = mm_serie
AND tm_documentNumber = mm_documentNumber
and my table class models:
public class testMag
{
[Key]
[Column(Order = 0)]
public string code{ get; set; }
[Key]
[Column(Order = 1)]
public string tm_type { get; set; }
[Key]
[Column(Order = 2)]
public short tm_year { get; set; }
[Key]
[Column(Order = 3)]
public string tm_serie { get; set; }
[Key]
[Column(Order = 4)]
public int tm_documentNumber { get; set; }
...
}
public class movMag
{
[Key]
[Column(Order = 0)]
public string code { get; set; }
[Key]
[Column(Order = 1)]
public string mm_type { get; set; }
[Key]
[Column(Order = 2)]
public short mm_year { get; set; }
[Key]
[Column(Order = 3)]
public string mm_serie { get; set; }
[Key]
[Column(Order = 4)]
public int mm_documentNumber { get; set; }
[Key]
[Column(Order = 5)]
public int mm_row { get; set; }
...
}
i'm trying these dapper query, with only one splitOn key and with all keys
string qJoin2 = "SELECT top 10 * FROM testmag INNER JOIN movmag ON movmag.code= testmag.code AND tm_type = mm_type AND tm_year = mm_year AND tm_serie = mm_serie AND tm_documentNumber = mm_documentNumber ";
var res = connection.Query<movmag, testmag, Tuple<movmag, testmag>>(
qJoin2,
Tuple.Create,
splitOn: "code,tm_type,tm_year,tm_serie,tm_documentNumber,code,mm_type,mm_year,mm_serie,mm_documentNumber,mm_row").ToList();
var res2 = connection.Query<movmag, testmag, Tuple<movmag, testmag>>(
qJoin2,
Tuple.Create,
splitOn: "code").ToList();
where i'm wrong?
someone could explain me!?
many thanks

i've changed type order in dapper.query()
and it works on both
var res = connection.Query<testmag, movmag, Tuple<testmag, movmag>>(
qJoin2,
Tuple.Create,
splitOn: "codditt,tm_tipork,tm_anno,tm_serie,tm_numdoc,codditt,mm_tipork,mm_anno,mm_serie,mm_numdoc,mm_riga").ToList();
var res2 = connection.Query<testmag, movmag, Tuple<testmag, movmag>>(
qJoin2,
Tuple.Create,
splitOn: "codditt").ToList();

Related

Dapper multi object query One-Many

Not really sure why I'm not getting the child object populated.
My tables:
Product:
[ProductId]
,[Brand]
,[Model]
StoreProduct:
[StoreId]
,[ProductId]
,[StoreProductId]
Class
public class Product
{
public Guid ProductId { get; set; }
public string Brand { get; set; }
public string Model { get; set; }
public virtual List<StoreProduct> StoreProducts { get; set; }
}
public class StoreProduct
{
public int StoreId { get; set; } //Key 0
public Guid ProductId { get; set; } //Key 1
public Store Store { get; set; }
public Product Product { get; set; }
public string StoreProductId { get; set; } //A new Id specific for each store
}
My Dapper Code
string sql = "SELECT * FROM StoreProduct AS A INNER JOIN Product AS B ON A.ProductId = B.ProductId WHERE A.StoreProductId = #StoreProductId and A.StoreId = #StoreId";
var connection = AppDbContext.Database.GetDbConnection();
return connection.Query<StoreProduct, Product, Product>(
sql,
(StoreProduct, Product) => { StoreProduct.ProductId = Product.ProductId; return Product; },
new { StoreProductId = storeProductId, StoreId = StoreID }, splitOn: "ProductId")
.FirstOrDefault();
What the DB returns:
But... StoreProducts List is null.
Use Dapper the way it works.
var listProduct = new Dictionary<string, Product>();
var listStoreProduct = new Dictionary<string, StoreProduct>();
using var connection = _connectionProvider.GetConnection();
var query = "SELECT * FROM StoreProduct AS A INNER JOIN Product AS B ON A.ProductId = B.ProductId WHERE A.StoreProductId = #StoreProductId and A.StoreId = #StoreId";
var result = connection.Query<Product, StoreProduct, Product>(query, (product, storeProduct) =>
{
if (!listProduct.TryGetValue(product.Id, out var entry))
{
entry = product;
listProduct.Add(entry.Id, entry);
}
if (storeProduct != null && !listStoreProduct.TryGetValue(storeProduct.Id, out var procInstance))
{
listStoreProduct.Add(procInstance.Id, procInstance);
entry.ProcessInstance.Add(procInstance);
}
return entry;
}, splitOn: "ProductId").Distinct().ToList();
I hope I could have helped you.

Populate two table data using ASP.NET Core

I want to get two table data in ASP.NET Core. I can get one table detail by using model class. then I can show data by using below code.
[HttpGet]
public async Task<ActionResult<IEnumerable<OrderMaster>>> GetOrderDetails()
{
return await _context.OrderDetails.ToListAsync();
}
So my question is how to get two tables data to the above method? As a example I want to retrieve data for below query:
select a.ItemDescription,a.Quantity,a.Amount, a.CustomerCode, b.CustomerName,b.CustomerAddress,b.MobileNumber,b.Email from OrderDetails as a left join CustomerDetails as b ON a.CustomerCode=b.CustomerCode
Thank you
My model classes
public class CustomerMaster
{
[Key]
public int CustomerCode { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string CustomerName { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string CustomerAddress { get; set; }
[Column(TypeName = "nvarchar(10)")]
public string MobileNumber { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string Email { get; set; }
}
public class OrderMaster
{
[Key]
public int OrderId { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string ItemCode { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string ItemName { get; set; }
[Column(TypeName = "nvarchar(MAX)")]
public string ItemDescription { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string Quantity { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string OrderDate { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string Amount { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string CustomerCode { get; set; }
}
this is my Context class
public class AppDbcontext : DbContext
{
public AppDbcontext(DbContextOptions<AppDbcontext> options) : base(options)
{
}
public DbSet<CustomerMaster> CustomerDetails { get; set; }
public DbSet<OrderMaster> OrderDetails { get; set; }
}
}
For how to join two tables,a simple demo you could follow:
var model = (from a in _context.CustomerDetails
join b in _context.OrderDetails
on a.ID equals b.ID
select new {
Name = a.Name,
Address = b.Address
}).ToList();
Update 1:
You use left join sql,you need change like below:
var model = (from a in _context.OrderDetails
join b in _context.CustomerDetails
on a.CustomerCode equals b.CustomerCode.ToString() into ab
from b in ab.DefaultIfEmpty()
select new
{
ItemDescription = a.ItemDescription,
Quantity = a.Quantity,
Amount = a.Amount,
CustomerCode = a.CustomerCode,
CustomerName = b.CustomerName,
CustomerAddress = b.CustomerAddress,
MobileNumber = b.MobileNumber,
Email = b.Email,
}).ToList();
Update 2:
For how to return the two tables data,I think a better way is to create a view model to display:
public class OrderDetailViewModel
{
public string ItemDescription { get; set; }
public string Quantity { get; set; }
public string Amount { get; set; }
public string CustomerCode { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
public string MobileNumber { get; set; }
public string Email { get; set; }
}
[HttpGet]
public IEnumerable<OrderDetailViewModel> GetOrderDetails()
{
var model = (from a in _context.OrderDetails
join b in _context.CustomerDetails
on a.CustomerCode equals b.CustomerCode.ToString() into ab
from b in ab.DefaultIfEmpty()
select new OrderDetailViewModel
{
ItemDescription = a.ItemDescription,
Quantity = a.Quantity,
Amount = a.Amount,
CustomerCode = a.CustomerCode,
CustomerName = b.CustomerName,
CustomerAddress = b.CustomerAddress,
MobileNumber = b.MobileNumber,
Email = b.Email,
}).ToList();
return model;
}
You have to get the two models connected through navigation properties. Something like
public class Table1 {
int Table1Id;
string Name;
ICollection<Table2> Tables2;
}
public class Table2 {
int Table2Id;
string Address;
Table1 Table1;
}
Then you can have your business layer code
[HttpGet]
... GetNameWithAddresses(int id) {
return (from c in _context.Table1 where c.Table1Id == id
select new { c.Name, c.Tables2 }).ToList();
}
If the tables have primary-foreign key relationship, it will be done automatically for you if you scaffold them. And if you don't have FK relationship, you have to ask yourself why!
Use this code
using (var context = new AppDbcontext())
{
var table1 = context.CustomerDetails.ToList();
var table2 = context.OrderDetails.ToList();
}

EF Core LINQ with conditional JOIN

I'm battling to transfer a SQL statement into a linq statement for my EF Core project. I'm probably battling with the navigation properties, but it seems what I am doing in SQL - won't be possible in EF Core maybe.
I have an Transaction, which has a DebitAccount and a Credit account. A transaction is made of 1:many TransactionLines. And this is where the amount is stored. So a transaction sums up the amounts to get the transaction value.
In EF, I have defined my tables, and attempted to link them correctly with navigation properties. These are how the three tables are declared.
Account Entity:
internal class Account : EntityAudit
{
[Key, Required, Column(Order = 0)]
public int Id { get; set; }
[Required, Column(Order = 1)]
public Guid ExternalId { get; set; }
[Required, Column(Order = 2)]
public AccountTypes AccountTypeId { get; set; }
[Required, Column(Order = 3)]
public UserAccount UserAccount { get; set; }
[Required, Column(Order = 4)]
public string Name { get; set; }
// Navigation Properties
public virtual ICollection<Transaction> TransactionDebitAccount { get; set; }
public virtual ICollection<Transaction> TransactionCreditAccount { get; set; }
}
Transaction Entity:
internal class Transaction : EntityAudit
{
[Key, Required, Column(Order = 1)]
public int Id { get; set; }
[Required, Column(Order = 2)]
public Guid ExternalId { get; set; }
[Required, Column(Order = 3)]
public DateTime Date { get; set; }
[Column(Order = 4)]
public string Description { get; set; }
[Required, Column(Order = 3), InverseProperty("TransactionDebitAccount")]
public virtual Account DebitAccount { get; set; } // Nav
[Required, Column(Order = 4), InverseProperty("TransactionCreditAccount")]
public virtual Account CreditAccount { get; set; } // Nav
[Required]
public virtual List<TransactionLine> TransactionLines { get; set; } //Nav
}
}
And the TransactionLine entity:
internal class TransactionLine
{
[Key, Required, Column(Order = 0)]
public int Id { get; set; }
[Required, Column(Order = 1)]
public Guid ExternalId { get; set; }
[Required, Column(Order = 2)]
public virtual Transaction Transaction { get; set; } // Nav
[Column(Order = 5), Range(0.01, 999999.99, ErrorMessage = "Amount must be between 0.01 and 999,999.99")]
public decimal Amount { get; set; }
[Column(Order = 6)]
public virtual Budget Budget { get; set; }
[Column(Order = 7)]
public virtual Subcategory Subcategory { get; set; }
}
I am now trying to get a list of account balances. I can achieve this with a SQL statement, which uses a conditional join, and this is maybe where I am stuck on EF Core.
FROM Account a
INNER JOIN [Transaction] t
ON (t.CreditAccountId = a.Id OR t.DebitAccountId = a.Id )
That conditional join is what is stopping me. I need to join the transactions to the account, based on one of two columns.
The whole query looks like this:
Select a.ExternalId AS ExternalId, a.Name,
SUM(tl.Amount *
case when t.CreditAccountId = a.Id then 1
when t.DebitAccountId = a.Id then -1 end)
as Amt
FROM Account a
INNER JOIN [Transaction] t
ON (t.CreditAccountId = a.Id OR t.DebitAccountId = a.Id )
INNER JOIN TransactionLine tl
ON tl.TransactionId = t.id
WHERE a.AccountTypeId = 1
GROUP BY a.Id, a.ExternalId, a.Name
I can't find a way to achieve this in EF Core using linq. Is this possible? Or would I need to restructure tables or maybe execute the SQL instead? I'd prefer to stay with EF Core / linq.
I am trying:
var accounts = (from a in context.Account
from t in context.Transaction
where t.DebitAccount.Id == a.Id || t.CreditAccount.Id == t.Id
from tl in context.TransactionLine
where tl.Transaction.Id == t.Id
select new { a.ExternalId, a.Name })
.GroupBy(x=> new { x.ExternalId, x.Name, })
But failing... The 'GroupBy' isn't available to me when trying this syntax.

Dapper Multi Mapping Result

I have two classes
public class Customer
{
public int CustomerId { get; set;}
public string CustomerName { get; set; }
}
public class Order
{
public int OrderId { get; set; }
public int CustomerId { get; set; } //BuyerCustomer
public int CustomerSecondId { get; set; } //ReceiverCustomer
public Customer BuyerCustomer { get; set; }
public Customer ReceiverCustomer { get; set; }
}
Here's my query will look like
SELECT a.*, b.*, c.* FROM dbo.PRODUCTS_ORDER a
INNER JOIN dbo.CUSTOMER b ON a.CustomerId=b.CustomerId
INNER JOIN dbo.CUSTOMER c ON a.CustomerSecondId=b.CustomerId
Dapper Implementation..
List<Order> order= null;
order= (List<Order>)dapperconnection.Query<Order, Customer, Customer, Order>(sql,
(order, customer1,customer2) =>
{
order.BuyerCustomer = customer1;
order.ReceiverCustomer = customer2;
return order;
}, splitOn: "CustomerId,CustomerSecondId ");
The result I'm getting is incomplete, only the RecevierCustomer gets populated while the BuyerCustomer doesn't contain any values at all.
It looks like dapper is confused since i used the CustomerId twice in my query.
Is there any workaround with this without having to change my the Customer class?
There are few issues with your class design and Dapper query.
Customer.CustomerName should be string
I would remove CustomerId and CustomerSecondId from Order. They are redundant. You have both Id's in the Customer.
Remove CustomerSecondId from Split.
Below is a working test:
public class Order
{
public int OrderId { get; set; }
public Customer BuyerCustomer { get; set; }
public Customer ReceiverCustomer { get; set; }
}
public class Customer
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
}
[Test]
public void TestSplitMany()
{
using (var conn = new SqlConnection(#"Data Source=.\sqlexpress;Integrated Security=true; Initial Catalog=foo"))
{
var result =
conn.Query<Order, Customer, Customer, Order>(#"select OrderId = 1, CustomerId = 2, CustomerName = 'Jim', CustomerId = 3, CustomerName = 'Bob'",
(order, buyer, receiver) =>
{
order.BuyerCustomer = buyer;
order.ReceiverCustomer = receiver;
return order;
}, splitOn: "CustomerId").First();
Assert.That(result.BuyerCustomer.CustomerId == 2);
Assert.That(result.ReceiverCustomer.CustomerId == 3);
Assert.That(result.BuyerCustomer.CustomerName == "Jim");
Assert.That(result.ReceiverCustomer.CustomerName == "Bob");
}
}

how to combine the result from differentviews to IQueryable<T>

i have a list of PlanItems bind to telerik RadGrid and have two views retrieves the result.Each view get different data so how can i get that data into IQueryable
public class PlanItems
{
public int Id{get; set;}
public string Name { get; set; }
public string HV { get; set; }
public string StNumber { get; set; }
public string LDNumber { get; set; }
public string MSId { get; set; }
public string CreatedBy { get; set; }
public string Type { get; set; }
}
I have 2 different views vw_Boys ,vw_Girls and Student Entity
first i got boys_Id and Girls_Id from student Entity
public IQuerable<PlanItems> GetAllStudents()
{
var Id = from v in context.Student
.Include("SBoy")
.Include("SGirl")
where v.CreatedBy_Id == user.Id
select v;
var temp = unp.Select(a=>a.SBoy.boys_Id).Distinct();
var temp1 = unp.Select(a => a.SGirl.girls_Id).Distinct();
var vwboys = from nv in context.vw_boys
where(temp.Contains(nv.SId))
select nv;
var vwgirls= from nv in context.vw_girls
where (temp1.Contains(nv.GId))
select nv;
var Boysresult = from n in vwboys
select new PlanItems
{
Id = n.SId,
Name = n.Name,
HV = n.HV,
StNumber = n.SNumber,
LDNumber = n.LineNumber,
MSId = n.MasterId,
CreatedBy = n.USerName,
Type = n.SubjectType
};
var GirlsResult = from n in vwgirls
select new UnplannedItems
{
Id = n.GiId,
Name = n.Name,
HV = n.GV,
StNumber = n.SujNumber,
LDNumber = n.Lid,
MSId = n.MasterId,
CreatedBy = n.USerName,
Type = n.SubjectType
};
}
my telerikGrid sample
<telerik:GridViewDataColumn Header="StudentID" IsReadOnly="True" DataMemberBinding="{Binding ID, Mdde=OneWay}" />
how can i return the result from above method..how can i combine the result to one.
I would do it something like this.
Add a new property to you class like this. Because you might have to seperate the boys from the girls in a other case:
public class PlanItems
{
public int Id{get; set;}
public string Name { get; set; }
public string HV { get; set; }
public string StNumber { get; set; }
public string LDNumber { get; set; }
public string MSId { get; set; }
public string CreatedBy { get; set; }
public string Type { get; set; }
//new property
public bool IsUnplanned { get; set; }
}
The use a concat between them like this:
var result= (
from n in vwboys
select new PlanItems
{
Id = n.SId,
Name = n.Name,
HV = n.HV,
StNumber = n.SNumber,
LDNumber = n.LineNumber,
MSId = n.MasterId,
CreatedBy = n.USerName,
Type = n.SubjectType,
IsUnplanned=false
}
).Concat
(
from n in vwgirls
select new PlanItems
{
Id = n.GiId,
Name = n.Name,
HV = n.GV,
StNumber = n.SujNumber,
LDNumber = n.Lid,
MSId = n.MasterId,
CreatedBy = n.USerName,
Type = n.SubjectType,
IsUnplanned=true
}
);
Hope this helps
You can union 2 different IQueryable using union extension method :
here's an example
I am not an expert in wpf, but looking at the solution I think you can use a parent class for both Boys and Girls and then add to a list of that class.
lets say both Boy and Girl classes are inherited from Person class, then create
List<Person> personList = new List<Person>()
and add Boys and Girls into this list.
I am not sure whether this is the solution you need

Resources