Starting from a table of daily fruit prices
fruits.csv
Day,Name,Kind,Price
2019-09-04,"apple","red",63.09
2019-09-04,"apple","yellow",52.14
2019-09-04,"orange","navel",41.18
2019-09-04,"orange","blood",41.18
2019-09-03,"apple","red",63.07
2019-09-03,"apple","yellow",52.11
2019-09-03,"orange","navel",41.13
2019-09-03,"orange","blood",41.13
I'd like to insert the reference prices by name and kind
fruit_ref_prices.csv
Name,Kind,Reference_Price
"apple","red",60.00
"apple","yellow",50.00
"orange","navel",40.00
"orange","blood",42.00
to result in the following table
Day,Name,Kind,Price,Reference_Price
2019-09-04,"apple","red",63.09,60.00
2019-09-04,"apple","yellow",52.14,50.00
2019-09-04,"orange","navel",41.18,40.00
2019-09-04,"orange","blood",41.18,42.00
2019-09-03,"apple","red",63.07,60.00
2019-09-03,"apple","yellow",52.11,50.00
2019-09-03,"orange","navel",41.13,40.00
2019-09-03,"orange","blood",41.13,42.00
The solution should be simple using C#'s built-in SQL-like syntax, and I'm sure the answer lies in one of the following tutorial pages:
Join clause
Perform custom join operations
Join by using composite keys
but I'm having a hard time identifying the syntax of this language.
In my attempt below instead of writing
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
I should be able to write
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
and fruit.kind equals fruit_ref.kind
but the Boolean expression is not accepted. Why?
My attempt is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
namespace MyConsoleApplication
{
class Program
{
const string root = #"c:\path\to\here\";
const string file1_in = root + #"fruits.csv";
const string file2_in = root + #"fruit_ref_prices.csv";
static void Main(string[] args)
{
Fruit_Basket fruit_basket = new Fruit_Basket(file1_in, file2_in);
fruit_basket.PrintFruits();
}
}
public class Fruit
{
public DateTime day { get; set; }
public string name { get; set; }
public string kind { get; set; }
public decimal price { get; set; }
public Fruit(DateTime newFruit_day,
string newFruit_name,
string newFruit_kind,
decimal newFruit_price)
{
this.day = newFruit_day;
this.name = newFruit_name;
this.kind = newFruit_kind;
this.price = newFruit_price;
}
}
public class Fruit_Ref
{
public string name;
public string kind;
public decimal reference_price;
public Fruit_Ref(string newName, string newKind, decimal newRef_Price)
{
this.name = newName;
this.kind = newKind;
this.reference_price = newRef_Price;
}
}
public class Fruit_Basket {
public List<Fruit> fruits { get; set; }
public List<Fruit_Ref> fruit_refs { get; set; }
public Fruit_Basket(string file1_in, string file2_in) {
build_fruit_list(file1_in);
build_fruit_ref_list(file2_in);
}
public void build_fruit_list(string file_in)
{
fruits = new List<Fruit>();
int count = 0;
StreamReader reader = new StreamReader(file_in);
string line = "";
while ((line = reader.ReadLine()) != null)
{
if (++count > 1)
{
string[] splitLine = line.Split(new char[] { ',' }).ToArray();
var newFruit_day = DateTime.Parse(splitLine[0]);
var newFruit_name = splitLine[1];
var newFruit_kind = splitLine[2];
var newFruit_price = decimal.Parse(splitLine[3]);
Fruit newFruit = new Fruit(newFruit_day,
newFruit_name,
newFruit_kind,
newFruit_price);
fruits.Add(newFruit);
}
}
reader.Close();
}
public void build_fruit_ref_list(string file_in)
{
fruit_refs = new List<Fruit_Ref>();
int count = 0;
StreamReader reader = new StreamReader(file_in);
string line = "";
while ((line = reader.ReadLine()) != null)
{
if (++count > 1)
{
string[] splitLine = line.Split(new char[] { ',' }).ToArray();
var newFruit_name = splitLine[0];
var newFruit_kind = splitLine[1];
var newFruit_ref_price = decimal.Parse(splitLine[2]);
Fruit_Ref newFruit_ref = new Fruit_Ref(newFruit_name,
newFruit_kind,
newFruit_ref_price);
fruit_refs.Add(newFruit_ref);
}
}
reader.Close();
}
public void PrintFruits()
{
var innerJoinQuery =
from fruit in fruits
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
select new { Day = fruit.day, Name = fruit.name, Kind = fruit.kind,
Price = fruit.price, Reference_Price = fruit_ref.reference_price };
Console.WriteLine($#"""Date"",""Name"",""Kind"",""Price"",""Ref Price""");
foreach (var i in innerJoinQuery)
{
Console.WriteLine($#"{i.Day},{i.Kind},{i.Price},{i.Reference_Price}");
}
}
}
}
You could also refactor your code to use the CsvHelper NuGet package for reading/writing CSV files.
First, You can make these classes to reflect the fruits, fruit references and final fruit structure.
public class Fruit
{
public string Day { get; set; }
public string Name { get; set; }
public string Kind { get; set; }
public string Price { get; set; }
}
public class FruitReferencePrice
{
public string Name { get; set; }
public string Kind { get; set; }
public string Reference_Price { get; set; }
}
public class FruitFinal
{
public string Day { get; set; }
public string Name { get; set; }
public string Kind { get; set; }
public string Price { get; set; }
public string ReferencePrice { get; set; }
public override string ToString()
{
return $"Day={Day},Name={Name},Kind={Kind},Price={Price},Reference_Price={ReferencePrice}";
}
}
Then you can make two methods to return the rows of each CSV file into List<Fruit> and List<FruitReferencePrice>.
private static IEnumerable<Fruit> BuildFruitList(string csvFilePath)
{
if (!File.Exists(csvFilePath))
{
throw new FileNotFoundException("Could not locate CSV at path " + csvFilePath, csvFilePath);
}
try
{
using var fileReader = File.OpenText(csvFilePath);
using var csv = new CsvReader(fileReader);
return csv.GetRecords<Fruit>().ToList();
} catch (Exception ex)
{
Console.WriteLine(ex.Message);
return Enumerable.Empty<Fruit>();
}
}
private static IEnumerable<FruitReferencePrice> BuildFruitReferenceList(string csvFilePath)
{
if (!File.Exists(csvFilePath))
{
throw new FileNotFoundException("Could not locate CSV at path " + csvFilePath, csvFilePath);
}
try
{
using var fileReader = File.OpenText(csvFilePath);
using var csv = new CsvReader(fileReader);
return csv.GetRecords<FruitReferencePrice>().ToList();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return Enumerable.Empty<FruitReferencePrice>();
}
}
Then you can perform a grouped join and output the merged result.
var path1 = "PATH\\fruits.csv";
var path2 = "PATH\\fruit_ref_prices.csv";
var fruitList = BuildFruitList(path1);
var fruitReferencePrices = BuildFruitReferenceList(path2);
var groupedJoin = from fruit in fruitList
join fruit_ref in fruitReferencePrices
on new { fruit.Name, fruit.Kind } equals new { fruit_ref.Name, fruit_ref.Kind }
select new FruitFinal
{
Day = fruit.Day,
Name = fruit.Name,
Kind = fruit.Kind,
Price = fruit.Price,
ReferencePrice = fruit_ref.Reference_Price
};
foreach (var fruit in groupedJoin)
{
Console.WriteLine(fruit.ToString());
}
Merged results:
Day=2019-09-04,Name=apple,Kind=red,Price=63.09,Reference_Price=60.00
Day=2019-09-04,Name=apple,Kind=yellow,Price=52.14,Reference_Price=50.00
Day=2019-09-04,Name=orange,Kind=navel,Price=41.18,Reference_Price=40.00
Day=2019-09-04,Name=orange,Kind=blood,Price=41.18,Reference_Price=42.00
Day=2019-09-03,Name=apple,Kind=red,Price=63.07,Reference_Price=60.00
Day=2019-09-03,Name=apple,Kind=yellow,Price=52.11,Reference_Price=50.00
Day=2019-09-03,Name=orange,Kind=navel,Price=41.13,Reference_Price=40.00
Day=2019-09-03,Name=orange,Kind=blood,Price=41.13,Reference_Price=42.00
Please change the equals clause as on new { fruit.name, fruit.kind } equals new { fruit_ref.name, fruit_ref.kind }
Why you require this
The query has two anonymous types (one for left table and one for right table). So to compare those anonymous types, the linq statement should use new keyword
Query :
var innerJoinQuery = from fruit in fruits
join fruit_ref in fruit_refs on new { fruit.name, fruit.kind } equals new { fruit_ref.name, fruit_ref.kind }
select new { Day = fruit.day, Name = fruit.name, Kind = fruit.kind,
Price = fruit.price, Reference_Price = fruit_ref.reference_price };
using (database db = new database())
{
var query = (
from c in db.tblBlogs
join a in db.tblBlogMedias on c.id equals a.BlogId
select new
{
c.id,
c.CreatedDate,
c.Author,
c.BlogTitle,
c.BlogDescription,
a.BlogId,
a.BlogPicturePath
}).OrderByDescending(d => d.id).ToList();
System.Text.StringBuilder sb = new System.Text.StringBuilder();
query.ToList().ForEach(x =>
{
sb.Append(string.Format("<div class='col-sm-4
wow fadeInUp animated' data-wow-duration='1000ms' data-wow-delay='400ms'
style='visibility: visible; animation-duration: 1000ms; animation-delay:
400ms; animation-name: fadeInUp;'><div class='post-thumb'><div class='post-
meta'>"+
"</div>"+
"</div>"+
"<div class='entry-
header'><h3><a href='#'>{0}</a></h3><span class='date'>{1}</span></div>
</div>",x.BlogTitle,x.CreatedDate));
});
}
How do I write this sql query in var query = :
select tblBlog.*,tblBlogMedia.BlogPicturePath from tblBlog left outer join
tblBlogMedia on tblBlog.id = tblBlogMedia.BlogId
where tblBlogMedia.id=(select max(id) from tblBlogMedia where BlogId='2')
With properly named entities and Navigation properties, it would look like this:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EfDbFirstTest
{
public class Blog
{
public int Id { get; set; }
public DateTime CreatedDate { get; set; }
public string Author { get; set; }
public string BlogTitle { get; set; }
public string BlogDescription { get; set; }
public ICollection<BlogMedia> BlogMedia { get; set; }
}
public class BlogMedia
{
public int Id { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
public string BlogPicturePath { get; set; }
}
public class Db : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<BlogMedia> BlogMedia { get; set; }
}
class Program
{
static void Main(string[] args)
{
using (var db = new Db())
{
db.Database.Log = m => Console.WriteLine(m);
var q = from b in db.Blogs
select new
{
b.Id,
b.CreatedDate,
b.Author,
b.BlogTitle,
b.BlogDescription,
BlogPicturePath = b.BlogMedia.Any() ? b.BlogMedia.OrderByDescending(m => m.Id).FirstOrDefault().BlogPicturePath : null
};
var results = q.ToList();
Console.ReadKey();
}
}
}
}
This is how it works perfectly as i want, I write this code in ForEach loop body and then pass the value of q in sb.append :
var q = (from d in db.tblBlogMedias
join c in db.tblBlogs on d.BlogId equals x.id
select new
{
d.Id,
d.BlogPicturePath
}).OrderByDescending(d=>d.Id).Max(d => d.BlogPicturePath);
I'm trying to create a List of object Work using Dapper to do the mapping.
This is the code:
public class Work
{
public int id { get; set; }
public int id_section { get; set; }
public decimal price { get; set; }
public Model id_model { get; set; }
public Type id_type { get; set; }
}
class Model
{
public int id_model { get; set; }
public string Name { get; set; }
}
class Type
{
public int id_type { get; set; }
public string Name { get; set; }
}
public List<Work> GetListOfWork(int idList)
{
using (DatabaseConnection db = new DatabaseConnection()) //return a connection to MySQL
{
var par = new {Id = idList};
const string query = "SELECT id,id_section,price,id_model,id_type FROM table WHERE id_section = #Id";
return db.con.Query<Work, Model, Type, Work>(query,
(w, m, t) =>
{
w.id_model = m;
w.id_type = t;
return w;
}, par, splitOn: "id_model,id_type").ToList();
}
}
It doesn't give me any error but id_model and id_type in my the returned List are always empty (The object are created but all the fields are empty or null), other fields are mapped correctly.
Any clue ?
You need to add yourself the joins in the query string
Probably it is something like this
var par = new {Id = idList};
const string query = #"SELECT w.id,w.id_section,w.price,
m.id_model, m.Name, t.id_type, t.Name
FROM work w INNER JOIN model m on w.id_model = m.id_model
INNER JOIN type t on w.id_type = t.id_type
WHERE w.id_section = #Id";
return db.con.Query<Work, Model, Type, Work>(query,
(w, m, t) =>
{
w.id_model = m;
w.id_type = t;
return w;
}, par, splitOn: "id_model,id_type").ToList();
I have a requirement to reflect on a object get all properties that are collections and
1)GetCount for each collection
2)GetTotalCount (allCollectionCount)
3)Call a method with this collection.
Below is what I have done so far with a made up noddy structure for semplicity.
I am stuck in how to call this method and how to get count for collection.
Any suggestions?
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main()
{
var request = GetDataRequest();
//Get all properties
List<PropertyInfo> propInfoList =
new List<PropertyInfo>(request.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public));
//Get collections only
var myClassCollections=propInfoList.Where(xxx => xxx.PropertyType.GetInterfaces().Any(x => x == typeof (IEnumerable))).ToList();
var totalCountForAllCollections=????
foreach (var col in myClassCollections)
{
//How do I call my Method DoSomething
// DoSomething<?>(col.?????)
}
}
public void DoSomething<T>(List<T> objectCollection)
{
//etc...
}
private static DataRequest GetDataRequest()
{
DataRequest request = new DataRequest();
request.Addresses.Add(new Address
{
Id = 1,
City = "London",
Postcode = "32131",
Street = "London Road"
});
request.Addresses.Add(new Address
{
Id = 2,
City = "NewYork",
Postcode = "3432",
Street = "NewYork Road"
});
request.Customers.Add(new Customer
{
Id = 1,
Name = "Jo",
Surname = "Bloggs",
});
request.Customers.Add(new Customer
{
Id = 1,
Name = "Jon",
Surname = "Bloggs2",
});
request.Customers.Add(new Customer
{
Id = 1,
Name = "Jonny",
Surname = "Bloggs3",
});
return request;
}
}
public class DataRequest
{
public DataRequest()
{
Customers = new List<Customer>();
Orders = new List<Order>();
Addresses = new List<Address>();
}
public List<Customer> Customers { get; set; }
public List<Order> Orders { get; set; }
public List<Address> Addresses { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
public class Order
{
public int Id { get; set; }
public string Name { get; set; }
public string OrderNo { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
}
quick and dirty, here you go...
// ..
static class Program
{
static void Main()
{
var request = GetDataRequest();
//Get propertyValues for properties that are enumerable (i.e. lists,arrays etc)
var collectionProperties = request.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(propertInfo => propertInfo.PropertyType.GetInterfaces().Any(x => x == typeof(IEnumerable)))
.Select(p => p.GetValue(request, null))
.Cast<IEnumerable<object>>().ToList();
var totalCountForAllCollections = 0;
// iterate through the list of propertyValues
foreach (var collectionPropertyValue in collectionProperties)
{
totalCountForAllCollections += collectionPropertyValue.Count();
collectionPropertyValue.DoSomething();
}
System.Console.WriteLine("The total count for all collections is : {0}", totalCountForAllCollections);
System.Console.WriteLine("press any key to exit");
System.Console.ReadLine();
}
public static void DoSomething<T>(this IEnumerable<T> objectCollection)
{
//etc...
// N.B. you will have to use typeof(T) to implement logic specific to the type
// If the logic in this method is non-specific to the typeof(T) then Implement logic accordingly
System.Console.WriteLine("The type of the collection is: {0}", objectCollection.GetType());
System.Console.WriteLine("The count of items in this collection is:{0}", objectCollection.Count());
}
// ..
}
// ..
I have following LINQ query:
var LINQFilter = (from Cash in _DataTable_Cash.AsEnumerable()
join CashOpeningsAssignments in _DataTable_CashOpeningsAssignments.AsEnumerable().Where(a => (a.Field<Int32>("cashopeningassignmentstatus_id") == 1 || a.Field<Int32>("cashopeningassignmentstatus_id") == 2))
on Cash.Field<Int32>("cash_id") equals CashOpeningsAssignments.Field<Int32>("cash_id") into into_cashopeningsassignments
from CashOpeningsAssignments in into_cashopeningsassignments.DefaultIfEmpty()
join Users in _DataTable_Users.AsEnumerable()
on CashOpeningsAssignments.Field<Int32>("user_id") equals Users.Field<Int32>("user_id") into into_users
from Users in into_users.DefaultIfEmpty()
select new
{
cash_id = Cash.Field<Int32>("cash_id"),
cellar_name = Cellars.Field<String>("cellar_name"),
cash_name = Cash.Field<String>("cash_name"),
cashstatus_name = CashStatus.Field<String>("cashstatus_name"),
user_name = (Users == null ? "[No Data]" : Users.Field<String>("user_firstname") + (Char)32 + Users.Field<String>("user_lastname")),
cashtransaction_amount = (Cash.Field<Int32>("cashstatus_id") == 2 ? 0.00 : 150.00)
});
I have problems showing the result because this Field returns null: CashOpeningsAssignments.Field<Int32>("user_id") when CashOpeningsAssignments is Empty.
I tried moving the .DefaultIfEmpty() into users but still not working, Any idea how i can solve this?
Answer
Use the overload of DefaultIfEmpty to create an empty item.
E.g.
into_cashopeningsassignments
.DefaultIfEmpty(new CashOpeningsAssignments())
Running Code
The code reflects what you're trying to do, even though I took some liberties with it. For instance, I used List<T> instead of DataTable, because I did not figure out how to use Field<T>(string name) in a DotNetFiddle.
It's live here: https://dotnetfiddle.net/YaAc6D
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var query =
from Cash
in _DataTable_Cash.AsEnumerable()
join CashOpeningsAssignments
in _DataTable_CashOpeningsAssignments.AsEnumerable()
.Where(a =>
(a.cashopeningassignmentstatus_id == 1 ||
a.cashopeningassignmentstatus_id == 2))
on Cash.cash_id
equals CashOpeningsAssignments.cash_id
into into_cashopeningsassignments
from CashOpeningsAssignments
in into_cashopeningsassignments.DefaultIfEmpty(new CashOpeningsAssignments())
join Users
in _DataTable_Users.AsEnumerable()
on CashOpeningsAssignments.user_id
equals Users.user_id
into into_users
from Users
in into_users.DefaultIfEmpty()
select new
{
cash_id = Cash.cash_id,
// cellar_name = Cellars.cellar_name,
cash_name = Cash.cash_name,
// cashstatus_name = CashStatus.cashstatus_name,
user_name = (Users == null ? "[No Data]" : Users.user_firstname + (Char)32 + Users.user_lastname),
cashtransaction_amount = (Cash.cashstatus_id == 2 ? 0.00 : 150.00)
};
foreach(var result in query)
{
Console.WriteLine(result);
}
}
public static List<Cash> _DataTable_Cash =
new List<Cash> { new Cash() };
public static List<Cellars> _DataTable_Cellars =
new List<Cellars> { new Cellars() };
public static List<CashStatus> _DataTable_CashStatus =
new List<CashStatus> { new CashStatus() };
public static List<CashOpeningsAssignments> _DataTable_CashOpeningsAssignments =
new List<CashOpeningsAssignments> { };
public static List<Users> _DataTable_Users =
new List<Users>() { new Users() };
}
public class Cash
{
public int cash_id { get; set; }
public string cash_name { get; set; }
public int cellar_id { get; set; }
public int cashstatus_id { get; set; }
}
public class Cellars
{
public string cellar_name { get; set; }
public int cellar_id { get; set; }
}
public class CashStatus
{
public int cashstatus_id { get; set; }
public string cashstatus_name { get; set; }
}
public class CashOpeningsAssignments
{
public int user_id { get; set; }
public int cash_id { get; set; }
public int cashopeningassignmentstatus_id { get; set; }
}
public class Users
{
public string user_firstname { get; set; }
public string user_lastname { get; set; }
public int user_id { get; set; }
}
See Also
https://msdn.microsoft.com/en-us/library/vstudio/bb355419%28v=vs.100%29.aspx