LINQ select new list, pass parent class to property class - c#

I'm not sure if I'm just searching the wrong thing or not, I just can't find an example of passing a parent class into a property of that class.
I've attempted to create a simple example which you can see below or view it at https://dotnetfiddle.net/uBGWgp.
I simply would like the Column class to know of its own parent Table, passing through the instance of it via the constructor (new Table()). See comment in code example where I wish to do this: /* Pass parent here? */
using System;
using System.Collections.Generic;
using System.Linq;
public class FakeDb{
public class FakeColumn{
public string ColumnName;
}
public class FakeTable{
public string TableName {get;set;}
public IEnumerable<FakeColumn> Columns {get;set;}
}
public IEnumerable<FakeTable> Tables {get;set;}
public FakeDb(){
Tables = new List<FakeTable>(){
new FakeTable(){
TableName = "People",
Columns = new List<FakeColumn>(){
new FakeColumn(){
ColumnName = "FirstName"
},
new FakeColumn(){
ColumnName = "LastName"
}
}
}
};
}
}
public class Table{
public Guid TableGuid = Guid.NewGuid();
public IEnumerable<Column> Columns;
}
public class Column{
private Table _myTable;
public Guid ColumneGuid = Guid.NewGuid();
public Column(Table hostTable){
_myTable = hostTable;
}
}
public class Program
{
public static void Main()
{
var fakeDb = new FakeDb();
var tableList = fakeDb.Tables.GroupBy(p => p.TableName, p => p, (tableName, values) => new {
TableName = tableName,
OtherValues = values
}).Select(p => new Table(){
Columns = p.OtherValues.Select(i => new Column(/* Pass parent here? */)).ToList()
}).ToList();
}
}

Because you are not holding a reference to the new Table object you are creating, there is no way to do it in a single-lined query. However changing it to a multi-lined lambda expression will make this possible. Save your newly created table in a variable and then pass that variable into your Column's constructor.
You will have to do something like this
.Select(p =>
{
var t = new Table();
t.Columns = p.OtherValues.Select(i => new Column(t)).ToList();
return t;
});
Demo: https://dotnetfiddle.net/1kKxMR

Related

How to return two specific columns from database using LINQ?

I have table called Asset. It has lot of columns. I only want to select two of them and use them separately.
Both of these columns are strings.
Linq query :
public static List<string> GetAssetIdsWithNames()
{
using (var db = DbManager.Get())
{
var result = db.Assets.SelectMany(i=> new[] { i.AssetName, i.AssetId }).Distinct().ToList();
return result;
}
}
Where I want to use them :
var assetList = AssetManager.GetAssetIdsWithNames();
//CURRENCYBOX IS A DROPDOWN
CurrencyBox.DataSource = assetList;
CurrencyBox.DataBind();
foreach (var item in assetList)
{
CurrencyBox.DataValueField = //asset id goes here
CurrencyBox.DataTextField =//asset name goes here
break;
}
You cannot access the anonymous type outside of the local scope.
Anonymous types can only be returned as Object outside their local scope and their properties inspected via reflection.
So in this scenario, you are likely better off to use a typed data contract and map from your Asset entity instead and then access it from your calling method.
Your use of SelectMany seems odd too, you probably are after Select instead.
public class AssetDto
{
public string Name { get;set; }
public string Id { get; set; }
}
public static List<AssetDto> GetAssetIdsWithNames()
{
using (var db = DbManager.Get())
{
var result = db.Assets.Select(i=> new AssetDto { Name = i.AssetName, Id = i.AssetId }).ToList();
return result;
}
}
You could use named value tuples for that so you don't need to create an extra class
public static List<(string Name, int Id)> GetAssetWithIds()
{
using (var db = DbManager.Get())
{
var result = db.Assets
.Select(a => new { a.AssetName, a.AssetId })
.Distinct().AsEnumerable()
.Select(a => (a.AssetName, a.AssetId))
.ToList();
return result;
}
}
You will need to add System.ValueTuple

Set property value from database

I'm trying to set property values straight from database so i could initiate new object
Object AllValues = new Object(); and access it in code like this AllValues.MediumRunTime;
using Entity Framework and it's returning 0's which it should't.
Also ReSharper is throwing Field value is never assigned warning
My table is Values{name, value, units} and contains around ~100 records.
This is my AllValues class
using System.Linq;
namespace RunTimes
{
public class AllValues
{
private long value;
public long ShortRunTime
{
get => value;
set
{
using (var data = new ModelData())
{
var prop = data.Values.FirstOrDefault(c => c.name == "ShortRunTime");
value = prop.value;
}
}
}
public long MediumRunTime
{
get => value;
set
{
using (var data = new ModelData())
{
var prop = data.Values.FirstOrDefault(c => c.name == "MediumRunTime");
value = prop.value;
}
}
}
...
And im trying to access it on Program.cs like this
AllValues av = new AllValues();
Console.WriteLine($"Medium run time: {av.MediumRunTime}");
A property setter is not the place to 'set a field with the value from the database', you use it to set the value of the property after you first loaded it from the database.
So instead of your AllValues class as it currently stands, you would have a class with some simple properties, such as:
class AllValues
{
public string ShortRunTime { get; set; }
public string MediumRunTime { get; set; }
// ...
}
and then instantiate it like this:
var av = new AllValues();
using (var data = new ModelData())
{
av.ShortRunTime = data.Values.FirstOrDefault(c => c.name == "ShortRunTime")?.Value;
av.MediumRunTime = data.Values.FirstOrDefault(c => c.name == "MediumRunTime")?.Value;
// ...
}
After this you can work with your class in memory. The 'trick' is to separate concerns: your 'transformed class' (AllValues) and loading data from the database are 2 different concerns that should be treated separately.

Linq contains for SQL Like %

Basically I'd like to use a LINQ statement for an sql like %. I thought .Contains would be appropriate but I don't get the right result.
I have a model:
public class Model {
public string Number { get; set; }
}
Inside my function:
private List<Model> getSearchResults(List<Model> models)
{
List<Model> result = models.Where(m => m.Number.Contains("3330")).ToList();
return result;
}
Inside my models list is definitively an entry with number 3330, but the returned result is empty. :(
This should also work with .Contains("333"). But it doesn't.
I suspect you're actually using the linq-to-sql provider.
If that's the case, then you should use SqlMethods.Like instead of String.Contains:
list.Where(l => SqlMethods.Like(l.Number, "%12%")).ToList();
Contains statements should be used like sql operator 'IN'. See the link for details:
tip-8-writing-where-in-style-queries-using-linq-to-entities
I think you are looking for something like this:
how-to-do-sql-like-in-linq
You should use like this:
List<Model> result = models.Where(m => m.Number.Contains("/3330/")).ToList();
You can also use .StartsWith() or .EndsWith()
Ok, here is my sample of code for you issue, and yes, the Contains method certainly does work:
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public class Model {
public string Number { get; set; }
}
private static List<Model> getSearchResults(List<Model> models)
{
List<Model> result = models.Where(m => m.Number.Contains("3330")).ToList();
return result;
}
public static void Main()
{
List<Model> list = new List<Model>() {
new Model() { Number = "13330A"},
new Model() { Number = "13230A"},
new Model() { Number = "3330A"},
new Model() { Number = "543330"},
new Model() { Number = "48913"},
new Model() { Number = "97798133"},
new Model() { Number = "542130"}
};
foreach(Model v in getSearchResults(list)) {
Console.WriteLine(v.Number);
}
}
}
and output is:
13330A
3330A
543330
Check your code, you can play with this example on http://dotnetfiddle.net/143UgC

Reflection on IQueryable<T> can't find property

I created a custom gridview control and exported it into a dll so I can reuse it. Inside the dll I created a function to get the DataSource, I'm trying to fill a dropdown from there but is failing.
So on my website I have this
public partial class _Management : System.Web.UI.Page
{
public class _ManagementHelper
{
public int ID;
public string CompanyName;
public string ResourceName;
}
protected void Page_Load(object sender, EventArgs e)
{
ucGridViewEx.DataSource = ucGridViewEx_Source();
ucGridViewEx.DataBind();
}
private List<dynamic> ucGridViewEx_Source()
{
var source = dl.ComapniesResources.Select(x => new _ManagementHelper
{
ID = x.ResourceID,
CompanyName = x.Supplier1.SupplierName,
ResourceName = x.Name
});
return ucGridViewEx.GridViewExDataSource(source);
}
Then the custom control inside the dll have this relevant code
public List<dynamic> GridViewExDataSource<T>(IQueryable<T> query)
{
foreach (var column in this.Columns)
{
var gridViewExColumn = column as ColumnEx;
if (gridViewExColumn != null
&& gridViewExColumn.SearchType == SearchTypeEnum.DropDownList)
{
gridViewExColumn.DropDownDataSource = query.GetDropDownDataSource(gridViewExColumn.DataField);
}
}
return ((IQueryable<dynamic>)query).ToList<dynamic>();
}
Function GetDropDownDataSource() is inside another extension class inside the same dll as the gridview
internal static List<ListItem> GetDropDownDataSource<T>(this IQueryable<T> query,
string dataField)
{
var ddlSource = new List<ListItem>();
// x =>
var xParameter = Expression.Parameter(typeof(T), "x");
// x.Property
var propery = typeof(T).GetProperty(dataField);
// x => x.Property
var columnLambda = Expression.Lambda(Expression.Property(xParameter, propery), xParameter);
return ddlSource;
}
Code fails in this where I'm assingning the value to columnLambda because property is null, not because it does not exist (it does) because is not getting any property. I tried with GetProperties() and is not returning anything.
Is curious than this is happening since I moved to the DataSource to select into _ManagementHelper. I was using a dynamic ( Select(x => new {}) ) on ucGridViewEx_Source() before and it worked perfectly. Please don't provide the solution to keep using the dynamic because I need to allow both types, with dynamic and using custom objects.
_ManagementHelper has no property. It just contains three fields (as far as you told us). So GetPrperty returns nothing. Change the members of _ManagementHelper to properties:
public class _ManagementHelper
{
public int ID { get; set; }
public string CompanyName { get; set; }
public string ResourceName { get; set; }
}
I see one bug --
var source = dl.ComapniesResources.Select(x => new _ManagementHelper
{
// ResourceID = x.ResourceID, this was the old code
ID = x.ResourceID, // fixed code
CompanyName = x.Supplier1.SupplierName,
ResourceName = x.Name
});
also, where is ListItem defined?

Generic custom linq filter

How can I create a custom generic linq filter that will check if the generic class contains a property name and returns the query...
I have something like:
public static IQueryable<T> GetAllByType<T>(
this IQueryable<T> customQuery, string seller) where T : class, new()
{
customQuery = customQuery.Where(i => i.GetType().Name == "TypeOfSeller");
return customQuery;
}
If the property type on the table T exists then I want to filter the expression using the string seller passed in as a parameter...
simply: return an expression which will filter this table by the seller param which could be "big", "small" etc.
I think this does what you describe.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
class Seller
{
public string Name { get; set; }
public string TypeOfSeller { get; set; }
}
class SomeOtherData
{
public string Name { get; set; }
}
static class Program
{
static void Main(string[] args)
{
List<Seller> sellers = new List<Seller>();
sellers.Add(new Seller() { Name = "A", TypeOfSeller = "Test" });
sellers.Add(new Seller() { Name = "B", TypeOfSeller = "Test" });
sellers.Add(new Seller() { Name = "C", TypeOfSeller = "Other" });
var q = from p in sellers.AsQueryable<Seller>().GetAllByType("Test") select p;
List<SomeOtherData> other = new List<SomeOtherData>();
other.Add(new SomeOtherData() { Name = "A" });
other.Add(new SomeOtherData() { Name = "B" });
other.Add(new SomeOtherData() { Name = "C" });
var q2 = from p in other.AsQueryable<SomeOtherData>().GetAllByType("Test") select p;
foreach (var x in q)
Console.WriteLine(x.Name + ", " + x.TypeOfSeller);
Console.WriteLine("Other Data: ");
foreach (var x in q2)
Console.WriteLine(x.Name);
Console.ReadLine();
}
public static IQueryable<T> GetAllByType<T>(this IQueryable<T> customQuery, string seller) where T : class, new()
{
var prop = typeof(T).GetProperty("TypeOfSeller");
if(prop != null)
customQuery = customQuery.Where(i => prop.GetValue(i, new object[] {}).ToString() == seller);
return customQuery;
}
}
}
The output is:
A, Test
B, Test
Other Data:
A
B
C
I would refactor it a bit so that there's no "if" involved but that I'm only sending the entities that qualify to the method.
The next thing you want to consider is that if there are multiple entity models that share a property name and you want to share logic regarding that property name, utilize the partial aspect of the code-generated classes to extend those classes and have each of them implement an interface.
interface ISeller
{
string TypeOfSeller { get; set; }
}
This will allow you to add the interface to the list of constraints on the method and also allow you to simply utilize the TypeOfSeller property directly without trying to resort to other methods (such as reflection).
The question is not very clear but sounds like you want something like this
public static IQueryable<T> GetAllByType<T>(
this IQueryable<T> customQuery, string seller) where T : class, new()
{
return from i in customQuery
let prop = typeof(T).GetProperty("SellerType")
where prop != null && prop.GetValue(i, null).Equals(seller)
select i;
}

Categories

Resources