Linq select fields - c#

Just experimenting with Linq:
DataClassesDataContext db = new DataClassesDataContext();
var q = from p in db.tblBlogEntries select p;
foreach (var customer in q)
{
Response.Write(customer.ID + " " + customer.title + "\n");
}
Works fine, but I can only seem to return 1 field, or all of them. How do I select say, p.ID and p.title, and nothing else?

you need a projection to an anonymous type to only return the "parts" that you want:
var q = from p in db.tblBlogEntries select new { p.ID, p.Title };
Alternatively you could also define a new class that only has the subset of the properties you want. This might be beneficial if you want to return instances of the projection, since anonymous types can only be used in local scope:
var q = from p in db.tblBlogEntries
select new BlogTitleAndId() { ID = p.ID, Title = p.Title };

Instead of "select p"
Use:
var q = from p in db.tblBlogEntries select new { Column1 = p.Column1, Column2 = p.Column2 };
The new{} makes a inline class, so you can select whichever columns you need.

Something like this...
DataClassesDataContext db = new DataClassesDataContext();
var q = from p in db.tblBlogEntries select new { Id = p.ID, Title = p.title };
foreach (var customer in q)
{
Response.Write(customer.ID + " " + customer.title + "\n");
}

var q = from p in db.tblBlogEntries select new {p.ID, p.Title}

How about this:
DataClassesDataContext db = new DataClassesDataContext();
var q = from p in db.tblBlogEntries select new { ID = p.ID, title = p.title };
foreach (var customer in q)
{
Response.Write(customer.ID + " " + customer.title + "\n");
}
This part new { ID = p.ID, title = p.title } creates an anonymous class with members ID and title. You can add members for any column you wish and the types will be deduced automatically. Getting all the fields isn't that big a deal though. This article provides more information and a sample similar t yours.

Related

Selecting from multiple lists in model and putting it in a dropdown

I am trying to populate my dropdownlist by doing this
var componentList = (from c in model.Components
select new
{
compID = c.ID,
compName = c.CompID + ":" + c.ComponentName
}).OrderBy(x => x.compName).ToList();
(from sc in model.ComponentSubComps
select new
{
compID = sc.ID,
compName = sc.SubCompID + ":" + sc.SubCompName
}).OrderBy(x => x.compName).ToList();
ViewBag.Components = new SelectList(componentList, "compID", "compName");
But it is only pulling the data from the model.Components list in the model. How can I make it so it also pulls from the model.ComponentSubComps list as well?
I don't know if you want to combine the components and sub-components or not. Combining them as a dangerous potential to mix up the IDs, assuming if they're in a separate table in your database and their primary keys are just incremental integer values.
This would become a serious bug if it's not being taken care of first. If their IDs could be the same, you might have to go with two dropdowns (one for components and one for sub-components) instead of one.
If you have to combine both of them, it sounds like you almost have to combine something uniquely with the IDs, assign them as different groups and parse the IDs when you receive from post back:
// Define the Groups
var componentGroup = new SelectListGroup { Name = "Component" };
var subComponentGroup = new SelectListGroup { Name = "SubComponent" };
var componentOptions = model.Components
.OrderBy(x => x.ComponentName)
.Select(x => new SelectListItem
{
Value = "c:" + x.CompID,
Text = x.CompID + ":" + x.ComponentName,
Group = componentGroup
});
var subComponentOptions = model.ComponentSubComps
.OrderBy(x => x.SubCompName)
.Select(x => new SelectListItem
{
Value = "sc:" + x.SubCompID,
Text = x.SubCompID + ":" + x.SubCompName,
Group = subComponentGroup
});
var dropdownOptions = new List<SelectListItem>();
dropdownOptions.AddRange(componentOptions);
dropdownOptions.AddRange(subComponentOptions);
Then when the user makes a selection and post the dropdown value back to the sever, you might have to parse the selected dropdown value and see if the ID belongs to component or sub-component.
If there is no difference between components and sub-components, then you can just combine them as easily as:
var componentOptions = model.Components
.OrderBy(x => x.ComponentName)
.Select(x => new SelectListItem
{
Value = x.CompID,
Text = x.CompID + ":" + x.ComponentName
});
var subComponentOptions = model.ComponentSubComps
.OrderBy(x => x.SubCompName)
.Select(x => new SelectListItem
{
Value = x.SubCompID,
Text = x.SubCompID + ":" + x.SubCompName
});
// This is your SelectList. I would highly recommend you build a strongly-typed
// view model and use it instead of ViewBag.
var dropdownOptions = new List<SelectListItem>();
dropdownOptions.AddRange(componentOptions);
dropdownOptions.AddRange(subComponentOptions);
You have two statements. The result from the first statement you store in componentList but the result from the second statement isn't stored anywhere.
Try to add the result from the second statement to componentList.
Probably List<T>.AddRange(IEnumerable<T>) is the best option.
Edit:
var componentList = (from c in model.Components
select new
{
compID = c.ID,
compName = c.CompID + ":" + c.ComponentName
}).OrderBy(x => x.compName).ToList();
var secondResult = (from sc in model.ComponentSubComps
select new
{
compID = sc.ID,
compName = sc.SubCompID + ":" + sc.SubCompName
}).OrderBy(x => x.compName).ToList();
componentList.AddRange(secondResult);
ViewBag.Components = new SelectList(componentList, "compID", "compName");
You can write the queries with entities and get:
var componentList = model.Components.Select(c => new Components
{
compID = c.ID,
compName = c.CompID + ":" + c.ComponentName
}).OrderBy(x => x.compName).ToList();
var componentList1 = model.ComponentSubComps.Select(c => new Components
{
compID = c.ID,
compName = c.SubCompID + ":" + c.SubCompName
}).OrderBy(x => x.compName).ToList();
componentList.AddRange(componentList1);
ViewBag.Components = new SelectList(componentList, "compID", "compName");

Counts of certain fields in linq query

This is my query, it works fine and gives me expected result;
var result2 = (from dt in disMudahaleTipiRepo
join dm in disMudahaleRepo on dt.Kodu equals dm.MudahaleKodu
join kr in kurumRepo on dm.CreatedKurumKodu equals kr.KurumKodu
join yu in userRepo on dm.CreatedBy equals yu.ID
group dt by new { yu.Ad, yu.Soyad, kr.KurumAdi } into grp
select new
{
AdSoyAd = grp.Key.Ad + " " + grp.Key.Soyad,
KurumAdi = grp.Key.KurumAdi,
KoduSayisi1 = grp.Select(s => s.Kodu).Where(w=>w== "401.030").Count(),
KoduSayisi2 = grp.Select(s => s.Kodu).Where(w=>w== "402.090").Count(),
KoduSayisi3 = grp.Select(s => s.Kodu).Where(w=>w== "406.020").Count(),
KoduSayisi4 = grp.Select(s => s.Kodu).Where(w=>w== "402.020").Count(),
KoduSayisi5 = grp.Select(s => s.Kodu).Where(w=>w== "406.070").Count()
...
});
only problem is there are 86 fields more(lol), so it will end up like that:
...
select new
{
AdSoyAd = grp.Key.Ad + " " + grp.Key.Soyad,
KurumAdi = grp.Key.KurumAdi,
KoduSayisi1 = grp.Select(s => s.Kodu).Where(w=>w== "401.030").Count(),
...
KoduSayisi91 = grp.Select(s => s.Kodu).Where(w=>w== "436.070").Count()
});
Instead of doing this, I tried to create a dictionary and keep as (Code, Number) its good in theory but how can I tell this to linq?
I don't think definition of entities is necessary here but let me share;
public class DisMudahaleTipi : Entity
{
public string Kodu { get; set; }
public string Name { get; set; }
}
public class DisMudahale : AuditableEntity
{
public string MudahaleKodu { get; set; }
}
You could try to use a subquery, but I don't have a database at hand to test if the following works:
var keys = ["401.300", .. ];
var result = (from dt in disMudahaleTipiRepo
join dm in disMudahaleRepo on dt.Kodu equals dm.MudahaleKodu
join kr in kurumRepo on dm.CreatedKurumKodu equals kr.KurumKodu
join yu in userRepo on dm.CreatedBy equals yu.ID
group dt by new { yu.Ad, yu.Soyad, kr.KurumAdi } into grp
select new
{
AdSoyAd = grp.Key.Ad + " " + grp.Key.Soyad,
KurumAdi = grp.Key.KurumAdi,
Kudos = (from key in keys
select new
{
Key = key,
Amount = grp.Count(w => w.Kudo == key)
}).ToList(),
}).ToList();
Note: In addition to the subquery I changed Select(s=>s.Kudo).Where(w=>w=="...").Count() to reduce Linq complexity and improve performance.

How can I access the result of dynamic linq with dynamic key/value pairs of IEnumerable?

I have a Dynamic Linq query, through which am trying to access grouped data, grouped on two columns, and 1 columns on which aggregation function is used.
var gFieldList = new List<string>() { "Supplier", "Country" };
var sFieldList = new List<string>() { "Sales"};
var gField = string.Join(", ", gFieldList.Select(x => "it[\"" + x + "\"] as " + x));
var sField = string.Join(", ", sFieldList.Select(y => "Sum(Convert.ToDouble(it[\""+y+"\"])) as "+y));
var dQueryResult = dataTable
.AsEnumerable()
.AsQueryable()
.GroupBy("new("+gField+")", "it")
.Select("new("+sField+",it.Key as Key, it as Data)");
To access the data from the dQueryResult am using the following code.
var groupedData = (from dynamic dat in dQueryResult select dat).ToList();
foreach (var group in groupedData)
{
var key = group.Key;
var sum = group.Sales;
MessageBox.Show("Supplier : " + key.Supplier + "Country : " + key.Country + " SALES : "+Sale.ToString());
}
The problem is
var gFieldList = new List<string>() { "Supplier", "Country" };
var sFieldList = new List<string>() { "Sales"};
keeps on changing. They both need to be dynamic, which will not allow me to access the values dynamically from the above mentioned code since for now it's been coded as key.Supplier, key.Country and group.Sales .
How can I make the iteration of the result of the dynamic query as dynamic?
I tried using
var groupedData = (from dynamic dat in dQueryResult select dat).ToList();
foreach (var group in groupedData)
{
var key = group.Key;
var sum = group.Sales;
foreach (var v in gFieldList)
{
MessageBox.Show(key.v);
}
}
But it's throwing an exception stating 'DynamicClass1' does not contain a definition for 'v'
you can use reflection like this
foreach (var v in gFieldList)
{
MessageBox.Show(key.GetType().GetProperty(v).GetValue(key,null));
...

Perform group and join using LINQ

Does anyone have any suggestions about how I might do this using just LINQ?
var statements = db.vwOutstandingStatements.ToList();
var amounts = (from s in db.vwOutstandingStatements
group s by s.Id into v
select new
{
Id = v.Key,
amount = v.Sum(a => a.Amount)
}).ToList();
List<vmVendorStatements> statementsToProcess = new List<vmVendorStatements>();
foreach (var amount in amounts)
{
var statement = statements.Find(s => s.Id == amount.Id);
statementsToProcess.Add(new vmVendorStatements()
{
Id = statement.Id,
PropertyAddress = statement.PropertyNumber + " " + statement.PropertyStreet + " " + statement.PropertyTown,
StatementDate = statement.StatementDate.ToLongDateString(),
Amount = amount.amount.ToString()
});
}
Statements is from a sql view via EF5. I run the LINQ to get the data grouped by the sum of the amounts in the returned view and then join it back to show some of the detail from the returned view along with the sums amounts. StatementsToView is my view model to get the data into an MVC view.
I'm sure it could be done in SQL, and I might do that in any case, but there also seems as though there must be a neater solution to the above too.
Thanks,
Jason.
You can just grab out the first item in the group rather than re-querying just to find the first item:
var statementsToProcess =
(from s in db.vwOutstandingStatements
group s by s.Id into v
let first = v.First()
select new vmVendorStatements()
{
Id = v.Key,
amount = v.Sum(a => a.Amount),
PropertyAddress = first.PropertyNumber + " " + first.PropertyStreet + " " + first.PropertyTown,
StatementDate = first.StatementDate.ToLongDateString(),
}).ToList();

SubSonic 3, build dynamic or expression at runtime

I have a situation where I have to dynamically build a linq query based on user selections.
If I had to dynamically generate sql I could do it this way:
var sb = new StringBuilder();
sb.AppendLine("SELECT * FROM products p");
sb.AppendLine("WHERE p.CategoryId > 5");
// these variables are not static but choosen by the user
var type1 = true;
var type2 = true;
var type3 = false;
string type1expression = null;
string type2expression = null;
string type3expression = null;
if (type1)
type1expression = "p.productType1 = true";
if (type2)
type2expression = "p.productType2 = true";
if (type3)
type3expression = "p.productType3 = true";
string orexpression = String.Empty;
foreach(var expression in new List<string>
{type1expression, type2expression, type3expression})
{
if (!String.IsNullOrEmpty(orexpression) &&
!String.IsNullOrEmpty(expression))
orexpression += " OR ";
orexpression += expression;
}
if (!String.IsNullOrEmpty(orexpression))
{
sb.AppendLine("AND (");
sb.AppendLine(orexpression);
sb.AppendLine(")");
}
// result:
// SELECT * FROM products p
// WHERE p.CategoryId > 5
// AND (
// p.productType1 = true OR p.productType2 = true
// )
Now I need to create a linq query the same way.
This works well with subsonic
var result = from p in db.products
where p.productType1 == true || p.productType2 == true
select p;
I tried it with PredicateBuilder http://www.albahari.com/nutshell/predicatebuilder.aspx but that throws an exception with subsonic.
var query = from p in db.products
select p;
var inner = PredicateBuilder.False<product>();
inner = inner.Or(p => p.productType1 == true);
inner = inner.Or(p => p.productType2 == true);
var result = query.Where(inner);
the exception that is thrown: NotSupportedException: The member 'productType1' is not supported
at SubSonic.DataProviders.MySQL.MySqlFormatter.VisitMemberAccess.
Anybody has an idea how to get this query to work:
Maybe Dynamic LINQ will be helpful?
Here is an usage example, as requested by geocine.
It requires Dynamic Linq.
var productTypes = new int[] {1,2,3,4};
var query = from p in db.products
select p;
if (productTypes.Contains(1))
query.Add("productType1 = #0");
if (productTypes.Contains(2))
query.Add("productType2 = #0");
if (productTypes.Contains(3))
query.Add("productType3 = #0");
if (productTypes.Contains(4))
query.Add("productType4 = #0");
if (productTypes.Count > 0)
{
string result = String.Join(" OR ", productTypes);
query = query.Where("(" + result + ")", true);
}
var result = from p in query
select new {Id = p.ProductId, Name = p.ProductName };
it looks akward but it works.

Categories