Nested foreach loops to LINQ - c#

Is it possible to rewrite this code to LINQ? I'm new to linq and it seems difficult to understand.
foreach (Employee employee in EmployeeList)
{
Earnings earnings = new Earnings(employee.Name, employee.LastName, employee.Bank, employee.Account);
if (!EarningsList.Contains(earnings))
{
EarningsList.Add(earnings);
}
foreach (DaysData item in ProductList)
{
foreach (Product product in item.Products)
{
if (product.EmployeeName == employee.Name && product.EmployeeLastName == employee.LastName)
{
double money = product.Count * product.Price;
earnings.AddMoney(money);
}
}
}
}

The first part isn't so easy to convert because of the conditional EarningsList.Add()
But You can rewrite the last 2 rather easily.
Assuming that AddMoney() does just what it says, you can use Sum(). Otherwise, omit the Sum() and run a separate foreach on the list of amounts. That would make it a lot less Linq.
var amount = ProductList
.SelectMany(item => item.Products)
.Where(product => product.EmployeeName == employee.Name && product.EmployeeLastName == employee.LastName)
.Sum(product => product.Count * product.Price)
;
earnings.AddMoney(amount);

Not knowing exactly what the .AddMoney method does you could do this:
var query =
from employee in EmployeeList
let earnings = new Earnings(employee.Name, employee.LastName, employee.Bank, employee.Account)
from item in ProductList
from product in item.Products
where product.EmployeeName == employee.Name
where product.EmployeeLastName == employee.LastName
group product.Count * product.Price by earnings;
List<Earnings> EarningsList =
query
.Aggregate(new List<Earnings>(), (a, x) =>
{
a.Add(x.Key);
foreach (var y in x)
{
x.Key.AddMoney(y);
}
return a;
});
If .AddMoney simply adds the money arithmetically then you could do this:
List<Earnings> EarningsList =
query
.Aggregate(new List<Earnings>(), (a, x) =>
{
a.Add(x.Key);
x.Key.AddMoney(x.Sum());
return a;
});
Just a small note. You're using double to represent money. It's best to use decimal as this will help prevent rounding errors in your calculations.

I think this is what you want if you just used LINQ(no foreach). This should be compatible with IQueryable as well and you really don't want to do foreach on IQueryable.
var newEarnings = from employee in EmployeeList
select new Earnings
{
Name = employee.Name,
LastName = employee.LastName,
Bank = employee.Bank,
Account = employee.Account,
Money = (from daysData in ProductList
from product in daysData.Products
where employee.Name == product.EmployeeName && employee.LastName == product.EmployeeLastName
select product).Sum(p => p.Count * p.Price)
};
EarningsList = EarningsList.Union(newEarnings).ToList();
Now in regards to normalization. My guess is that you made you POCO models like this in order to show it in some sort of a grid. You really should not let your UI dictate what you data models look like. There can be others reasons to do this but they are related to performance and I don't think you need to worry about this just jet. So here is my advice on how to change this.
Add Id property to all the classes. This is always a good idea no
matter what you are doing. This can be a random string or an auto
increment, just to have a unique value so you can play with this
object.
Add reference properties in you classes. Don't copy the values from
Employee to Product and Earnings. Just add a Property of type
Employee and/or add the EmployeeId property
So your POCO should look something like this
public class Employee
{
//It can be a Guid, string or what ever. I am not nesseserly recomending using Guids and you should look into this a bit more
public Guid Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public string Bank { get; set; }
public string Account { get; set; }
}
public class DaysData
{
public Guid Id { get; set; }
public IEnumerable<Product> Products { get; set; }
}
public class Product
{
public Guid Id { get; set; }
public Guid EmployeeId { get; set; }
public Employee Employee { get; set; }
public double Count { get; set; }
public double Price { get; set; }
}
public class Earnings
{
public Guid Id { get; set; }
public Guid EmployeeId { get; set; }
public Employee Employee { get; set; }
public double Money { get; set; }
}
And the query
var newEarnings = from employee in EmployeeList
select new Earnings
{
Id = Guid.NewGuid(),
EmployeeId = employee.Id,
Employee = employee,
Money = (from daysData in ProductList
from product in daysData.Products
where employee.Id == product.EmployeeId
select product).Sum(p => p.Count * p.Price)
};
Once you try to implement data persistence this will help you a lot no matter what you use EF, Mongo, Elsatic, AzureTables or anything else even a simple saving to files.

Related

Calculated property for model EF Core - Property or Method?

I am new to EF core. I have a Customer model with the usual properties (Name,Address,Email).
I need a property to calculate the current balance for the customer.
This will be quite an intensive computation (once many records are stored) so am I correct in thinking that it should be stored in a Method, rather than a calculated property?
I am assuming I need to add a method such as .GetCurrentBalance().
Where would I put this method?
Simplified code below:
My Customer Model
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
public virtual IEnumerable<SalesInvoice> SalesInvoices{ get; set; }
}
My Sales Invoice Model
public class SalesInvoice
{
public int SalesInvoiceId { get; set; }
public int CustomerId { get; set; }
public virtual IEnumerable<SalesInvoiceDetail> SalesInvoiceDetails{ get; set; }
}
My Sales Invoice Detail Model
public class SalesInvoiceDetail
{
public int SalesInvoiceDetailId { get; set; }
public int Qty { get; set; }
public decimal UnitPrice { get; set; }
}
Create helper methods which returns desired results. Everything should play around IQueryable:
public class CustomerIdWithBalance
{
public int CustomerId { get; set; }
public decimal Balance { get; set; }
}
public class CustomerWithBalance
{
public Customer Customer { get; set; }
public decimal Balance { get; set; }
}
public static class BusinessLogicExtensions
{
public static IQueryable<CustomerIdWithBalance> GetCustomerIdAndBalance(this IQueryable<Customer> customers)
{
var grouped =
from c in customers
from si in c.SalesInvoices
from sid in si.SalesInvoiceDetails
group sid by new { c.CustomerId } into g
select new CustomerIdWithBalance
{
g.Key.CustomerId,
Balance = x.Sum(x => x.Qty * x.UnitPrice)
}
return grouped;
}
public static IQueryable<CustomerWithBalance> GetCustomerAndBalance(this IQueryable<CustomerIdWithBalance> customerBalances, IQueryable<Customer> customers)
{
var query =
from b in customerBalances
join c in customers on b.CustomerId equals c.CustomerId
select new CustomerWithBalance
{
Customer = c,
Balance = b.Balance
};
return query;
}
}
Later when you need to return that with API call (hypothetic samples)
var qustomerIdsWithHighBalance =
from c in ctx.Customers.GetCustomerIdAndBalance()
where c.Balance > 1000
select c.CustomerId;
var qustomersWithHighBalance =
ctx.Customers.GetCustomerIdAndBalance()
.Where(c => c.Balance > 1000)
.GetCustomerAndBalance(ctx.Customers);
var customersByMatchAndPagination = ctx.Customers
.Where(c => c.Name.StartsWith("John"))
.OrderBy(c => c.Name)
.Skip(100)
.Take(50)
.GetCustomerAndBalance(ctx.Customers);
You will get desired results without additional database roundtrips. With properties you may load too much data into the memory.
It is everything about using EF with its limitations. But world is not stopped because EF team is too busy to create performance effective things.
Let's install https://github.com/axelheer/nein-linq
And create extension methods around Customer
public static class CustomerExtensions
{
[InjectLambda]
public static TotalBalance(this Customer customer)
=> throw new NotImplmentedException();
static Expression<Func<Customer, decimal>> TotalBalance()
{
return customer =>
(from si in customer.SalesInvoices
from sid in si.SalesInvoiceDetails
select sid)
.Sum(x => x.Qty * x.UnitPrice));
}
}
And everything become handy:
var customersWithHighBalance =
from c in ctx.Customers.ToInjectable()
where c.TotalBalance() > 1000
select c;
var customersWithHighBalance =
from c in ctx.Customers.ToInjectable()
let balance = c.TotalBalance()
where balance = balance > 1000
select new CustomerWithBalance
{
Customer = c,
Balance = balance
};
var customersWithBalance =
from c in ctx.Customers.ToInjectable()
where c.Name.StartsWith("John")
select new CustomerWithBalance
{
Customer = c,
Balance = c.TotalBalance()
};
var paginated =
.OrderBy(c => c.Name)
.Skip(100)
.Take(50);
If you would prefer to calculate at property level. Add an InvoiceBal readonly field to the SalesInvoice model.
public class SalesInvoice
{
public double InvoiceBal => SalesInvoiceDetails.Sum(x => x.Qty * x.UnitPrice)
public virtual IEnumerable<SalesInvoiceDetail> SalesInvoiceDetails{ get; set; }
}
Add another TotalBalance readonly field to the Customer that sums the whole thing
public class Customer
{
public double TotalBal => SalesInvoices.Sum(x => x.InvoiceBal)
public virtual IEnumerable<SalesInvoice> SalesInvoices{ get; set; }
}

How to query data with calculated fields and map to a ViewModel with a nested ViewModel Collection?

I have referenced numerous questions on this site related to calculated fields and ViewModels, but I can't seem to extrapolate from examples given. I hope that laying out a specific scenario would allow someone to pin point what I can't see. I am new to WebApp design in general. Please take that into consideration. Also, if I've left off any relevant information, please let me know and I will update the question.
Here is the scenario:
I have a complex query that is spanning multiple tables to return data used in calculations. Specifically, I store units for a recipe converted to a base unit and then convert the quantity to the units specified by the user.
I am using AutoMapper to map from entities to ViewModels and vice versa, but I am not sure how to handle the calculated values. Especially with the nested ViewModel Collection thrown into the mix.
Option 1
Do I return an autonomous set of data? Like the following... and then somehow use AutoMapper to do the mapping? Perhaps I would need to do the mapping manually, which I haven't found a solid example which includes nested ViewModels. At this point, I'm not even sure if the following code handles the nested collection correctly for the autonomous data.
var userId = User.Identity.GetUserId();
var recipes = from u in db.Users.Where(u => u.Id == userId)
from c in db.Categories
from r in db.Recipes
join ur in db.UserRecipes.Where(u => u.UserId == userId) on r.Id equals ur.RecipeId
join mus in db.MeasUnitSystems on ur.RecipeYieldUnitSysId equals mus.Id
join muc in db.MeasUnitConvs on mus.Id equals muc.UnitSysId
join mu in db.MeasUnits on mus.UnitId equals mu.Id
join msy in db.MeasUnitSymbols on mu.Id equals msy.UnitId
select new
{
Id = c.Id,
ParentId = c.ParentId,
Name = c.Name,
Descr = c.Descr,
Category1 = c.Category1,
Category2 = c.Category2,
Recipes = new
{
Id = r.Id,
Title = r.Title,
Descr = r.Descr,
Yield = String.Format("{0} {1}", ((r.Yield * muc.UnitBaseConvDiv / muc.UnitBaseConvMult) - muc.UnitBaseConvOffset), msy.Symbol)
}
};
Option 2
Another option that crossed my mind was to return the entities and use AutoMapper as I normally would. Then iterate through the collections and perform the calculations there. I feel like I could make this work, but it seems inefficient to me because it would result in many queries back to the database.
Option 3
???? I can't think of any other method to do this. But, please, if you have suggestions, I am more than willing to hear them.
Relevant Data
Here is the query returning the data I want in SQL Server (more or less).
declare #uid as nvarchar(128) = 'da5435ae-5198-4690-b502-ea3723a9b217'
SELECT c.[Name] as [Category]
,r.Title
,r.Descr
,(r.Yield*rmuc.UnitBaseConvDiv/rmuc.UnitBaseConvMult)-rmuc.UnitBaseConvOffset as [Yield]
,rmsy.Symbol
FROM Category as c
inner join RecipeCat as rc on c.Id = rc.CategoryId
inner join Recipe as r on rc.RecipeId = r.Id
inner join UserRecipe as ur on r.Id = ur.RecipeId and ur.UserId = #uid
inner join MeasUnitSystem as rmus on ur.RecipeYieldUnitSysId = rmus.Id
inner join MeasUnitConv as rmuc on rmus.Id = rmuc.UnitSysId
inner join MeasUnit as rmu on rmus.UnitId = rmu.Id
inner join MeasUnitSymbol as rmsy on rmu.Id = rmsy.UnitId
inner join UserUnitSymbol as ruus on rmsy.UnitId = ruus.UnitId and rmsy.SymIndex = ruus.UnitSymIndex and ruus.UserId = #uid
ViewModels
public class CategoryRecipeIndexViewModel
{
public int Id { get; set; }
public int ParentId { get; set; }
[Display(Name = "Category")]
public string Name { get; set; }
[Display(Name = "Description")]
public string Descr { get; set; }
public ICollection<CategoryRecipeIndexViewModel> Category1 { get; set; }
public CategoryRecipeIndexViewModel Category2 { get; set; }
public ICollection<RecipeIndexViewModel> Recipes { get; set; }
}
public class RecipeIndexViewModel
{
public int Id { get; set; }
[Display(Name = "Recipe")]
public string Title { get; set; }
[Display(Name = "Description")]
public string Descr { get; set; }
[Display(Name = "YieldUnit")]
public string Yield { get; set; }
}
UPDATE 2/10/2018
I found an answer here that does a very good job of explaining exactly what I'm looking at. Particularly under the A Better solution ? section. Mapping queries directly to my ViewModels looks like it would allow me to get my calculated values as well. Problem is, the example given is once again too simplistic.
He gives the following DTO's
public class UserDto
{
public int Id {get;set;}
public string Name {get;set;}
public UserTypeDto UserType { set; get; }
}
public class UserTypeDto
{
public int Id { set; get; }
public string Name { set; get; }
}
And does the following for mapping:
var users = dbContext.Users.Select(s => new UserDto
{
Id = s.Id,
Name = s.Name,
UserType = new UserTypeDto
{
Id = s.UserType.Id,
Name = s.UserType.Name
}
});
Now what if the UserDTO looked like this:
public class UserDto
{
public int Id {get;set;}
public string Name {get;set;}
public ICollection<UserTypeDto> UserTypes { set; get; }
}
How would the mapping be done if the UserTypes were a collection?
Update 2/13/2018
I feel I am making progress, but am currently headed in the wrong direction. I found this and came up with the following (which currently errors because of the method call in the linq query):
*Note: I removed Category2 from the ViewModel as I found it was not needed and only complicated this further.
query inside index controller method
IEnumerable<CategoryRecipeIndexViewModel> recipesVM = db.Categories
.Where(x => x.ParentId == null)
.Select(x => new CategoryRecipeIndexViewModel()
{
Id = x.Id,
ParentId = x.ParentId,
Name = x.Name,
Descr = x.Descr,
Category1 = MapCategoryRecipeIndexViewModelChildren(x.Category1),
Recipes = x.Recipes.Select(y => new RecipeIndexViewModel()
{
Id = y.Id,
Title = y.Title,
Descr = y.Descr
})
});
Recursive Method
private static IEnumerable<CategoryRecipeIndexViewModel> MapCategoryRecipeIndexViewModelChildren(ICollection<Category> categories)
{
return categories
.Select(c => new CategoryRecipeIndexViewModel
{
Id = c.Id,
ParentId = c.ParentId,
Name = c.Name,
Descr = c.Descr,
Category1 = MapCategoryRecipeIndexViewModelChildren(c.Category1),
Recipes = c.Recipes.Select(r => new RecipeIndexViewModel()
{
Id = r.Id,
Title = r.Title,
Descr = r.Descr
})
});
}
At this point, I don't even have the calculations I require, but that doesn't matter until I get this working (small steps). I quickly discovered you can't really call a method inside a Linq Query. Then a thought occurs to me, if I need to force the Linq Query to execute and then perform all the mapping on the in memory data, then I would essentially be doing the same thing as Option 2 (above), but I could perform the calculations within the ViewModel. This is the solution I will pursue and will keep everyone posted.
You have to iterate over UserType Collection and map the value to UserType dto's collection.
Use this code.
var users = dbContext.Users.Select(s => new UserDto
Id = s.Id,
Name = s.FullName,
UserType = s.UserType.Select(t => new UserTypeDto
{
Id = t.Id,
Name = t.Name
}).ToList()
Hope this will help.
I got it working! ...I think. ...Maybe. If anything, I'm querying the data, mapping it to my ViewModels and I have the calculations too. I do have additional questions, but they are a lot more specific. I will layout the solution I followed and where I think it requires work below.
I basically implemented my Option 2 from above, but instead of iterating through the collections, I just performed the calculations within the ViewModels.
Controller Method
public ActionResult Index()
{
var userId = User.Identity.GetUserId();
var recipes = db.Categories.Where(u => u.Users.Any(x => x.Id == userId))
.Include(c => c.Category1)
.Include(r => r.Recipes
.Select(u => u.UserRecipes
.Select(s => s.MeasUnitSystem.MeasUnitConv)))
.Include(r => r.Recipes
.Select(u => u.UserRecipes
.Select(s => s.MeasUnitSystem.MeasUnit.MeasUnitSymbols)));
IEnumerable<CategoryRecipeIndexViewModel> recipesVM = Mapper.Map<IEnumerable<Category>, IEnumerable<CategoryRecipeIndexViewModel>>(recipes.ToList());
return View(recipesVM);
}
View Models
public class CategoryRecipeIndexViewModel
{
public int Id { get; set; }
public int ParentId { get; set; }
[Display(Name = "Category")]
public string Name { get; set; }
[Display(Name = "Description")]
public string Descr { get; set; }
public ICollection<CategoryRecipeIndexViewModel> Category1 { get; set; }
public ICollection<RecipeIndexViewModel> Recipes { get; set; }
}
public class RecipeIndexViewModel
{
public int Id { get; set; }
[Display(Name = "Recipe")]
public string Title { get; set; }
[Display(Name = "Description")]
public string Descr { get; set; }
public double Yield { get; set; }
public ICollection<UserRecipeIndexViewModel> UserRecipes { get; set; }
[Display(Name = "Yield")]
public string UserYieldUnit
{
get
{
return System.String.Format("{0} {1}", ((Yield *
UserRecipes.FirstOrDefault().MeasUnitSystem.MeasUnitConv.UnitBaseConvDiv /
UserRecipes.FirstOrDefault().MeasUnitSystem.MeasUnitConv.UnitBaseConvMult) -
UserRecipes.FirstOrDefault().MeasUnitSystem.MeasUnitConv.UnitBaseConvOffset).ToString("n1"),
UserRecipes.FirstOrDefault().MeasUnitSystem.MeasUnit.MeasUnitSymbols.FirstOrDefault().Symbol);
}
}
}
public class UserRecipeIndexViewModel
{
public MeasUnitSystemIndexViewModel MeasUnitSystem { get; set; }
}
public class MeasUnitSystemIndexViewModel
{
public MeasUnitIndexViewModel MeasUnit { get; set; }
public MeasUnitConvIndexViewModel MeasUnitConv { get; set; }
}
public class MeasUnitIndexViewModel
{
public ICollection<MeasUnitSymbolIndexViewModel> MeasUnitSymbols { get; set; }
}
public class MeasUnitConvIndexViewModel
{
public double UnitBaseConvMult { get; set; }
public double UnitBaseConvDiv { get; set; }
public double UnitBaseConvOffset { get; set; }
}
public class MeasUnitSymbolIndexViewModel
{
public string Symbol { get; set; }
}
This appears to be working, but I know it needs some work.
For instance, the relation shown between the Recipe and UserRecipe shows one to many. In reality, if the UserRecipe were filtered by the current user, the relationship would be one to one. Also, the same goes for the MeasUnit and the MeasUnitSymbol entities. Currently, I'm relying on the FirstOrDefault of those collections to actually perform the calculations.
Also, I have seen numerous posts that state that calculations should not be done in the View Models. Except for some who say it's okay if it is only a requirement of the View.
Last I will say that paying attention to variable names within the ViewModels would have saved me some headaches. And I thought I knew how to utilize Linq Queries, but had issues with the data returned. It was easier to rely on the eager loading provided by Entity Framework to bring back the hierarchical data structure needed, versus the flat table structures I'm used to working with.
I'm still new to a lot of this and wrapping my head around some of the quirks of MVC and Entity Framework leaves me brain dead after a few hours, but I will continue to optimize and adopt better programming methods as I go.

Dapper: mapping hierarchy and single different property

I really love Dapper's simplicity and possibilities. I would like to use Dapper to solve common challenges I face on a day-to-day basis. These are described below.
Here is my simple model.
public class OrderItem {
public long Id { get; set; }
public Item Item { get; set; }
public Vendor Vendor { get; set; }
public Money PurchasePrice { get; set; }
public Money SellingPrice { get; set; }
}
public class Item
{
public long Id { get; set; }
public string Title { get; set; }
public Category Category { get; set; }
}
public class Category
{
public long Id { get; set; }
public string Title { get; set; }
public long? CategoryId { get; set; }
}
public class Vendor
{
public long Id { get; set; }
public string Title { get; set; }
public Money Balance { get; set; }
public string SyncValue { get; set; }
}
public struct Money
{
public string Currency { get; set; }
public double Amount { get; set; }
}
Two challenges have been stumping me.
Question 1:
Should I always create a DTO with mapping logic between DTO-Entity in cases when I have a single property difference or simple enum/struct mapping?
For example: There is my Vendor entity, that has Balance property as a struct (otherwise it could be Enum). I haven't found anything better than that solution:
public async Task<Vendor> Load(long id) {
const string query = #"
select * from [dbo].[Vendor] where [Id] = #id
";
var row = (await this._db.QueryAsync<LoadVendorRow>(query, new {id})).FirstOrDefault();
if (row == null) {
return null;
}
return row.Map();
}
In this method I have 2 overhead code:
1. I have to create LoadVendorRow as DTO object;
2. I have to write my own mapping between LoadVendorRow and Vendor:
public static class VendorMapper {
public static Vendor Map(this LoadVendorRow row) {
return new Vendor {
Id = row.Id,
Title = row.Title,
Balance = new Money() {Amount = row.Balance, Currency = "RUR"},
SyncValue = row.SyncValue
};
}
}
Perhaps you might suggest that I have to store amount & currency together and retrieve it like _db.QueryAsync<Vendor, Money, Vendor>(...)- Perhaps, you are right. In that case, what should I do if I need to store/retrive Enum (OrderStatus property)?
var order = new Order
{
Id = row.Id,
ExternalOrderId = row.ExternalOrderId,
CustomerFullName = row.CustomerFullName,
CustomerAddress = row.CustomerAddress,
CustomerPhone = row.CustomerPhone,
Note = row.Note,
CreatedAtUtc = row.CreatedAtUtc,
DeliveryPrice = row.DeliveryPrice.ToMoney(),
OrderStatus = EnumExtensions.ParseEnum<OrderStatus>(row.OrderStatus)
};
Could I make this work without my own implementations and save time?
Question 2:
What should I do if I'd like to restore data to entities which are slightly more complex than simple single level DTO? OrderItem is beautiful example. This is the technique I am using to retrieve it right now:
public async Task<IList<OrderItem>> Load(long orderId) {
const string query = #"
select [oi].*,
[i].*,
[v].*,
[c].*
from [dbo].[OrderItem] [oi]
join [dbo].[Item] [i]
on [oi].[ItemId] = [i].[Id]
join [dbo].[Category] [c]
on [i].[CategoryId] = [c].[Id]
join [dbo].[Vendor] [v]
on [oi].[VendorId] = [v].[Id]
where [oi].[OrderId] = #orderId
";
var rows = (await this._db.QueryAsync<LoadOrderItemRow, LoadItemRow, LoadVendorRow, LoadCategoryRow, OrderItem>(query, this.Map, new { orderId }));
return rows.ToList();
}
As you can see, my question 1 problem forces me write custom mappers and DTO for every entity in the hierarchy. That's my mapper:
private OrderItem Map(LoadOrderItemRow row, LoadItemRow item, LoadVendorRow vendor, LoadCategoryRow category) {
return new OrderItem {
Id = row.Id,
Item = item.Map(category),
Vendor = vendor.Map(),
PurchasePrice = row.PurchasePrice.ToMoney(),
SellingPrice = row.SellingPrice.ToMoney()
};
}
There are lots of mappers that I'd like to eliminate to prevent unnecessary work.
Is there a clean way to retrive & map Order
entity with relative properties like Vendor, Item, Category etc)
You are not showing your Order entity but I'll take your OrderItem as an example and show you that you don't need a mapping tool for the specific problem (as quoted). You can retrieve the OrderItems along with the Item and Vendor info of each by doing the following:
var sql = #"
select oi.*, i.*, v.*
from OrderItem
inner join Item i on i.Id = oi.ItemId
left join Vendor v on v.Id = oi.VendorId
left join Category c on c.Id = i.CategoryId";
var items = connection.Query<OrderItem, Item, Vendor, Category, OrderItem>(sql,
(oi,i,v,c)=>
{
oi.Item=i;oi.Item.Category=c;oi.Vendor=v;
oi.Vendor.Balance = new Money { Amount = v.Amount, Currency = v.Currency};
return oi;
});
NOTE: The use of left join and adjust it accordingly based on your table structure.
I'm not sure I understand your question a 100%. And the fact that no one has attempted to answer it yet, leads me to believe that I'm not alone when I say it might be a little confusing.
You mention that you love Dapper's functionality, but I don't see you using it in your examples. Is it that you want to develop an alternative to Dapper? Or that you don't know how to use Dapper in your code?
In any case, here's a link to Dapper's code base for your review:
https://github.com/StackExchange/dapper-dot-net
Hoping that you'd be able to clarify your questions, I'm looking forward to your reply.

Linq Query with join on subquery

Basically I'm trying to write a query where it joins on select top 1 from a second table so something like:
SELECT Sum(pinfo.quantity + p.itemcount),
i.owner
FROM invoice i
JOIN purchase_info pinfo
ON pinfo.invoice = i.invid
JOIN (SELECT DISTINCT sku,
productlineid,
itemcount
FROM products WHERE productlineid in (13, 14)) p
ON p.sku = pinfo.item
WHERE i.owner = 22623
GROUP BY i.owner
Here's my pathetic attempt in linq that has somewhat invalid syntax, any ideas would be much appreciated.
(from i in _invoiceRepository.Table
join pi in _purchaseInfoRepository.Table on i.InvoiceId equals pi.InvoiceId
join p in (from p2 in _productRepository.Table where p2.Sku == pi.Item select new { p2.Sku, p2.ItemCount }).Take(1)
on pi.Item equals p.Sku
where i.MemberId == memberId &&
(p.ProductLineId == (int)ProductLines.InkCartridges ||
p.ProductLineId == (int)ProductLines.TonerCartridges)
select pi.Quantity * p.ItemCount)
.DefaultIfEmpty(0)
.Sum();
Here is my first stab at this.
From the sql, it looks like you want to find how many Ink and Toner Cartridges a particular customer has ordered from you ever.
This should give you the same results as the sql (this is depending on the order of the Products table since we are taking the top 1 without some sort of ordering being done:
var count = from i in _invoiceRepository.Table
where i.OwnerId == memberId
select new
{
OwnerId = i.OwnerId,
TotalProductCount = i.Purchases.Sum(pro => pro.Products
.Where(p => p.ProductLineId == (int)ProductLines.InkCartridges ||
p.ProductLineId == (int)ProductLines.TonerCartridges)
.Take(1)
.Sum(p => p.ItemCount * pro.Quantity))
};
Since I did not know the the classes of the three objects (Invoice, PurchaseInfo, and Product) I made a guess at what they are:
Invoice Class: I assume it has a list/collection of PurchaseInfos
public class Invoice
{
public int Id { get; set; }
public int OwnerId { get; set; }
public List<PurchaseInfo> Purchases { get; set; }
}
PurchaseInfos: An invoice has multiple PurchaseInfos, each one links to (ideally) one product but since the SKU is not unique I assome that this has a list/collection of Products in it.
public class PurchaseInfo
{
public int Id { get; set; }
public int Quantity { get; set; }
public int InvoiceId { get; set; }
public Invoice Invoice { get; set; }
public int Item {get;set;}
public List<Product> Products { get; set; }
}
Product Class: I assome that there is an Id field (not shown) or a composite primary key somewhere
public class Product
{
public int Sku { get; set; }
public int ProductLineId { get; set; }
public int ItemCount { get; set; }
public List<PurchaseInfo> PurchaseInfos { get; set; }
}
Hopefully you can take this a get what you need. If this is way off, please update question with the class definitions (you can remove unneeded properities if you want) so a better answer can be produced.

Complex graphing in entity framework

I can't seem to find the correct where clause to get only the items I need.
I have Divisions, these contain Categories en these contain Items.
These are the classes:
public class Division {
public string Description { get; set; }
public List<Category> Categories { get; set; }
}
public class Category : IdEntity
{
public string Description { get; set; }
public Guid DivisionId { get; set; }
public List<Item> Items { get; set; }
}
public class Item
{
public Guid CategoryId { get; set; }
public DateTime Date { get; set; }
public string Description { get; set; }
}
What I need is a division with the Id in a parameter, all the categories from this division and the items that have a certain date for each category.
So right now I do this:
public Division GetFullDivisionByIdAndDate(Guid id, DateTime date)
{
using (new ChangeTrackingScope(ChangeTracking.Disabled))
{
var divisionGraph = new Graph<Division>().Include(d => d.Categories.Select(c => c.Items));
var division = _divisionDL.GetFullDivisionByIdAndDate(id, divisionGraph, date);
return division;
}
}
And than in the DL I do
public Division GetFullDivisionByIdAndDate(Guid id, Graph<Division> graph, DateTime date)
{
using (var db = new ContextScope<DatabaseContext>())
{
var q = graph.ApplySetReferences(db.Context.Divisions).AsNoTracking();
return q.SingleOrDefault(p => p.Id == id);
}
}
Here I get the division with all its categories (so far so good) but I also get all items and I need only the items with the date given as parameter. Anyone has an idea how to do this?
Your code is not very accessible because of a few missing methods (Graph, ApplySetReferences) so I can't hook into it. But I can show a common way to query an object graph, which is by navigation properties. In you model, a basic query body could look like this:
from d in Divisions
from c in d.Categories
from i in c.Items
select new { Div = d.Description, Cat = c.Description, Item = i.Description }
Starting from here you can add other filters and properties, like
from d in Divisions.Where(div => div.Id == id)
from c in d.Categories
from i in c.Items.Where(item => item.Date == date)
select new { ... }
Or if you want a result with items in a filtered collection:
from d in Divisions.Where(div => div.Id == id)
from c in d.Categories
select new new { Div = d.Description,
Cat = c.Description,
Items = c.Items.Where(item => item.Date == date)
}

Categories