Put LINQ query result to DataTable - c#

I have this query:
var smallExchangeReport = from ex in exchangeProgReport
where !string.IsNullOrEmpty(ex.comment)
group ex by new { ex.siteName } into g
select new SummuryReportTraffic
{
siteName = g.Key.siteName,
exchangeCounter = g.Where(x => x.Prog1ToProg2Check == 1).Count(),
descriptions = (from t in g
group t by new { t.comment, t.siteName } into grp
select new Description
{
title = grp.Key.comment,
numbers = grp.Select(x => x.comment).Count()
})
};
At some point I put it to the dataTable using foreach loop:
foreach (var item in smallExchangeReport)
{
dr = smrTable.NewRow();
foreach (var d in item.descriptions)
{
dr[d.title] = d.numbers;
}
smrTable.Rows.Add(dr);
}
But I need to put the LINQ result to dataTable without using foreach loop.
So I made some changes to my code above according to this link:
DataTable dt = new DataTable();
DataRow dr = dt.NewRow();
IEnumerable<DataRow> smallExchangeReport = from ex in exchangeProgReport.AsEnumerable()
where !string.IsNullOrEmpty(ex.comment)
group ex by new { ex.siteName } into g
select new
{
siteName = g.Key.siteName,
exchangeCounter = g.Where(x => x.Prog1ToProg2Check == 1).Count(),
descriptions = (from t in g.AsEnumerable()
group t by new { t.comment, t.siteName } into grp
select new
{
title = grp.Key.comment,
numbers = grp.Select(x => x.comment).Count()
})
};
// Create a table from the query.
DataTable boundTable = smallExchangeReport.CopyToDataTable<DataRow>();
But on changed LINQ query I get this error:
Cannot implicitly convert type:'System.Collections.Generic.IEnumerable<<anonymous type: string siteName, int exchangeCounter>>' to
'System.Collections.Generic.IEnumerable<System.Data.DataRow>'. An explicit conversion exists (are you missing a cast?)
My question is how to cast the query to make it work?I tryed to cast to(DataRow) the result of the LINQ but it didn't worked.

In your LINQ query, you are trying to get IEnumerable<DataRow> as the result, but actually you select new objects of an anonymous type: select new { siteName = .... }. This cannot work because your anonymous type cannot be cast to DataRow.
What you need to do is use a function that would populate a DataRow like this:
DataRow PopulateDataRow(
DataTable table,
string siteName,
int exchangeCounter,
IEnumerable<Description> descriptions
{
var dr = table.NewRow();
// populate siteName and exchangeCounter
// (not sure how your data row is structured, so I leave it to you)
foreach (var d in descriptions)
{
dr[d.title] = d.numbers;
}
return dr;
}
then in your LINQ query, use it as follows:
IEnumerable<DataRow> smallExchangeReport =
from ex in exchangeProgReport.AsEnumerable()
where !string.IsNullOrEmpty(ex.comment)
group ex by new { ex.siteName } into g
select PopulateDataRow(
smrTable,
siteName: g.Key.siteName,
exchangeCounter: g.Where(x => x.Prog1ToProg2Check == 1).Count(),
descriptions: (from t in g.AsEnumerable()
group t by new { t.comment, t.siteName } into grp
select new Description {
title = grp.Key.comment,
numbers = grp.Select(x => x.comment).Count()
}
)
);
This solution gets rid of one foreach (on rows) and leaves the other one (on descriptions).
If removing the second foreach is important... I would still leave it inside PopulateDataRow. I don't see an elegant way to remove it. You can call a method from LINQ query which reads like a deterministic function, but actually creates the side effect of setting a column value on a data row, but it doesn't feel right to me.

this is can help you.
defining table structure.
DataTable tbl = new DataTable();
tbl.Columns.Add("Id");
tbl.Columns.Add("Name");
and we need to create datarow from anonymous type.
Func<object, DataRow> createRow = (object data) =>
{
var row = tbl.NewRow();
row.ItemArray = data.GetType().GetProperties().Select(a => a.GetValue(data)).ToArray();
return row;
};
test with fake query:
var enumarate = Enumerable.Range(0, 10);
var rows = from i in enumarate
select createRow( new { Id = i, Name = Guid.NewGuid().ToString() });
var dataTable = rows.CopyToDataTable<DataRow>();

You can use this method:
private DataTable ListToDataTable<T>(List<T> objs, string tableName) {
var table = new DataTable(tableName);
var lists = new List<List<object>>();
// init columns
var propertyInfos = new List<PropertyInfo>();
foreach (var propertyInfo in typeof(T).GetProperties()) {
propertyInfos.Add(propertyInfo);
if(propertyInfo.PropertyType.IsEnum || propertyInfo.PropertyType.IsNullableEnum()) {
table.Columns.Add(propertyInfo.Name, typeof(int));
} else {
table.Columns.Add(propertyInfo.Name, Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType);
}
table.Columns[table.Columns.Count - 1].AllowDBNull = true;
}
// fill rows
foreach(var obj in objs) {
var list = new List<object>();
foreach(var propertyInfo in propertyInfos) {
object currentValue;
if(propertyInfo.PropertyType.IsEnum || propertyInfo.PropertyType.IsNullableEnum()) {
var val = propertyInfo.GetValue(obj);
if(val == null) {
currentValue = DBNull.Value;
} else {
currentValue = (int)propertyInfo.GetValue(obj);
}
} else {
var val = propertyInfo.GetValue(obj);
currentValue = val ?? DBNull.Value;
}
list.Add(currentValue);
}
lists.Add(list);
}
lists.ForEach(x => table.Rows.Add(x.ToArray()));
return table;
}
Edit:
this extension method is used:
public static bool IsNullableEnum(this Type t) {
var u = Nullable.GetUnderlyingType(t);
return u != null && u.IsEnum;
}

Related

Pass query result to method (Linq, Wpf)

I have method to save all values from table to txt file:
UserDataDBsDataContext dataContext = new UserDataDBsDataContext();
List<UserData> usersL = (from u in dataContext.UserDatas
select u).ToList();
var properties = typeof(UserData).GetProperties();
var userValues = new List<string>();
foreach (var user in usersL)
{
var values = new List<object>();
foreach (var property in properties)
{
object value = property.GetValue(user, null);
values.Add(value);
}
userValues.Add(string.Join(",", values));
}
File.WriteAllLines("my_data.txt", userValues);
Now I have two query and I want to do exactly the same, so I tried to create separate method responsible for looping table values.
Loop Method:
public void loopProp(PropertyInfo[] properites, List<string> addedValues)
{
foreach (var qrl in ...........)
{
var values = new List<object>();
foreach (var property in properites)
{
object value = property.GetValue(qrl, null);
values.Add(value);
}
addedValues.Add(string.Join(",", values));
}
File.WriteAllLines("my_passed_data.txt", addedValues);
}
But I don't know, how to pass query result(ar or ud):
My code:
List<AutoRef> ar = (from a in rjdc.AutoRefs
select a).ToList();
List<UserDataRef> ud = (from u in rjdc.UserDataRefs
select u).ToList();
var propertiesAutoRef = typeof(AutoRef).GetProperties();
var autoValues = new List<string>();
var propertiesUserRef = typeof(UserDataRef).GetProperties();
var userValues = new List<string>();
//loopProp(propertiesAutoRef, autoValues);
//loopProp(propertiesUserRef, userValues);
Answering your concrete question. You should make the method generic and pass the source as IEnumerable<T>:
public void loopProp<T>(IEnumerable<T> source, PropertyInfo[] properites, List<string> addedValues)
{
foreach (var qrl in source)
{
// ...
}
File.WriteAllLines("my_passed_data.txt", addedValues);
}
Usage:
loopProp(ar, propertiesAutoRef, autoValues);
loopProp(ud, propertiesUserRef, userValues);
Probably you should pass the file path argument as well instead of hardcoding it inside the method.

How to avoid repeating when I get data from stored procedure?

I get a list from stored procedure with its child date I want to avoid repeating when I add items to the the list
Here is my code:
var cmd = db.Database.Connection.CreateCommand();
cmd.CommandText = "[dbo].[T]";
db.Database.Connection.Open();
var reader1 = cmd.ExecuteReader();
var objectContext1 = ((IObjectContextAdapter)db).ObjectContext;
var usersLi = objectContext1.Translate<GetAllNewsForUser>(reader1).ToList();
List<GetAllNewsForUser> lll = new List<GetAllNewsForUser>();
foreach (var item in usersLi)
{
NewsInfo newsInfo = new NewsInfo();
GetAllNewsForUser g = new GetAllNewsForUser();
g.UserName = item.UserName;
NewsInfo nnn = new NewsInfo();
nnn.NewsTitle = item.NewsTitle;
nnn.NewsId = item.NewsId;
g.NewsInfos.Add(nnn);
lll.Add(g);
}
are there any unique identifiers for g, for example a user Id. in which case you could easily use the following:
if (!lll.Any(x=>x.UserId ==g.UserId)) //include system.linq
{
lll.Add(g);
}
You could also look at modifying your stored procedure to not return duplicates.
EDIT ----
Just twigged as to what you actually after, sorry.....
Its just a rough draft but i think this is what you require.
List<GetAllNewsForUser> lll = new List<GetAllNewsForUser>();
foreach (var item in usersLi)
{
NewsInfo nnn = new NewsInfo();
nnn.NewsTitle = item.NewsTitle;
nnn.NewsId = item.NewsId;
if (!lll.Any(x=> x.UserName == item.UserName)
{
GetAllNewsForUser g = new GetAllNewsForUser();
g.UserName = item.UserName;
g.NewsInfos.Add(nnn);
lll.Add(g);
}
else
{
lll.Where(x=>x.UserName == item.Username).FirstOrDefault().NewsInfos.Add(nnn)
}
}
From what I understand so far, you are trying to avoid adding same rows in the list(based on value of some column).
LINQ might help here.
usersLi.GroupBy(e => new {
UserID = e.UserUD
}).Select(g => g.First());

Outter joins in LINQ query

This code works good it takes and matches all the unit_no and vehiclename together but it only shows the matches i also need the ones that dont match on.
I am loading a datagrid in WPF with SQLserver data and another Datagrid from oracle data.
private void GetSQLOraclelinqData()
{
var TstarData = GetTrackstarTruckData();
var M5Data = GetM5Data();
DataTable ComTable = new DataTable();
foreach (DataColumn OraColumn in M5Data.Columns)
{
ComTable.Columns.Add(OraColumn.ColumnName, OraColumn.DataType);
}
foreach (DataColumn SQLColumn in TstarData.Columns)
{
if (SQLColumn.ColumnName == "VehicleName")
ComTable.Columns.Add(SQLColumn.ColumnName + "2", SQLColumn.DataType);
else
ComTable.Columns.Add(SQLColumn.ColumnName, SQLColumn.DataType);
}
var results = M5Data.AsEnumerable().Join(TstarData.AsEnumerable(),
a => a.Field<String>("Unit_No"),
b => b.Field<String>("VehicleName"),
(a, b) =>
{
DataRow row = ComTable.NewRow();
row.ItemArray = a.ItemArray.Concat(b.ItemArray).ToArray();
ComTable.Rows.Add(row);
return row;
}).ToList();

How I get the index of a DataTable Row with two calues of two other Column Values?

To My Application:
I have two DataTables and I want to filter out the first DataTable to the second. For this I get the columns user and modul from the first DataTable and if it not exist in the second I add a new row.
This is the structure from my second DataTable:
User | Modul | Time | Department | Status
and I want to check two Columns (User and Modul) whether the row with this values in the second DataTable exist. If the Entry Exist I need the row index. How I can do this with Linq?
The name of my second DataTable is analyse_table.
here my code:
private static DataTable FilterDataTable(DataTable nofilter_datatable)
{
DataTable analyse_table = new DataTable("Filter_Analyse");
DataColumn User = new DataColumn("User", typeof(string));
DataColumn Modul = new DataColumn("Modul", typeof(string));
DataColumn TIME = new DataColumn("TIME", typeof(string));
DataColumn Department = new DataColumn("Department", typeof(string));
DataColumn Status = new DataColumn("Status", typeof(string));
analyse_table.Columns.Add(User);
analyse_table.Columns.Add(Modul);
analyse_table.Columns.Add(TIME);
analyse_table.Columns.Add(Department);
analyse_table.Columns.Add(Status);
foreach (DataRow nf_row in nofilter_datatable.Rows)
{
string user = nf_row["User"].ToString();
string modul = nf_row["Modul"].ToString();
string OUT = nf_row["OUT"].ToString();
string IN = nf_row["IN"].ToString();
bool contains_user = analyse_table.AsEnumerable()
.Any(row => user == row.Field<string>("User"));
bool contains_modul = analyse_table.AsEnumerable()
.Any(Row => modul == Row.Field<string>("Modul"));
if (!contains_user || !contains_modul)
{
try
{
DataRow row = analyse_table.NewRow();
row["User"] = user;
row["Modul"] = modul;
if (OUT != string.Empty)
{
row["TIME"] = OUT;
row["Status"] = "OUT";
}
else if (IN != string.Empty)
{
row["TIME"] = IN;
row["Status"] = "IN";
}
string[] userSpli = user.Split('#');
row["Department"] = GetActiveDirectoryAttribute(userSpli[0], "Department", domaincontroller);
analyse_table.Rows.Add(row);
}
catch (Exception)
{
}
}
if (contains_user && contains_modul)
{
//index??
//string status = analyse_table.Rows[0]["Status"].ToString();
}
}
return analyse_table;
}
I need help.
There is no need to know the index in the analyse_table, using FirstOrDefault should allow you to find directly the row required
var rowUser = analyse_table.AsEnumerable()
.FirstOrDefault(row => user == row.Field<string>("User"));
var rowModul = analyse_table.AsEnumerable()
.FirstOrDefault(Row => modul == Row.Field<string>("Modul"));
if (rowUser == null || rowModul == null)
{
// Not exist so I add a new row
}
if (rowUser != null && rowModul != null)
{
string statusUser = rowUser["Status"].ToString();
string statusModul = rowModul["Status"].ToString();
}
However, having executed two different queries to search for your rows, we have no guarantees that the two rows are the same. So perhaps you need to change your code to search for both user and modul in the same row
var rowResult = analyse_table.AsEnumerable()
.FirstOrDefault(row => (user == row.Field<string>("User") &&
modul == row.Field<string>("Modul"));
if(rowResult == null)
// add new
else
// read status
I assume you want to add all missing rows that are in the first table but not in the second, the rows are identified via two columns User + Modul.
You could use a "LEFT OUTER JOIN" to link both tables on an anonymous type containing these columns. This is much more efficient.
var newRowsInFirst = from r1 in first_table.AsEnumerable()
join r2 in analyse_table.AsEnumerable()
on new { User=r1.Field<string>("User"), Modul=r1.Field<string>("Modul") }
equals new { User=r2.Field<string>("User"), Modul=r2.Field<string>("Modul") }
into gj from g2 in gj.DefaultIfEmpty()
where g2 == null
select r1;
Then use a simple foreach-loop to add these rows.
foreach(var newRow in newRowsInFirst)
{
analyse_table.ImportRow(newRow);
// or:
//DataRow addedRow = analyse_table.Rows.Add();
//addedRow.ItemArray = newRow.ItemArray;
}

What can be used instead of Datatable in LINQ

I have a SQL query that returns a Datatable:
var routesTable = _dbhelper.Select("SELECT [RouteId],[UserId],[SourceName],[CreationTime] FROM [Routes] WHERE UserId=#UserId AND RouteId=#RouteId", inputParams);
and then we can work with Datatable object of routesTable
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
result.TrackPoints = GetTrackPointsForRoute(routeId);
}
I want to change this code to linq but I don't know how can I simulate Datatable in LINQ ,I wrote this part:
Route result = null;
aspnetdbDataContext aspdb = new aspnetdbDataContext();
var Result = from r in aspdb.RouteLinqs
where r.UserId == userId && r.RouteId==routeId
select r;
....
but I don't know how can I change this part:
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
would you please tell me how can I do this?
EDIT
here you can see the whole block of code in original
public Route GetById(int routeId, Guid userId)
{
Route result = null;
var inputParams = new Dictionary<string, object>
{
{"UserId", userId},
{"RouteId", routeId}
};
var routesTable = _dbhelper.Select("SELECT [RouteId],[UserId],[SourceName],[CreationTime] FROM [Routes] WHERE UserId=#UserId AND RouteId=#RouteId", inputParams);
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
result.TrackPoints = GetTrackPointsForRoute(routeId);
}
return result;
}
SELECT Function:
public DataTable Select(string query, Dictionary<string, object> parameters)
{
var dt = new DataTable();
using (_command = new SqlCommand(query, _connnection))
{
InitializeParametersAndConnection(parameters);
using (_adapter = new SqlDataAdapter(_command))
{
_adapter.Fill(dt);
}
}
return dt;
}
and the GetTrackPointsForRoute
private List<TrackPoint> GetTrackPointsForRoute(int routeId)
{
aspnetdbDataContext aspdb = new aspnetdbDataContext();
var result = new List<TrackPoint>();
var trackPointsTable = from t in aspdb.TrackPointlinqs
where t.RouteFK == routeId
select t;
foreach (var trackPointRow in trackPointsTable)
{
var trackPoint = new TrackPoint
{
Id = (int)trackPointRow.TrackPointId,
Elevation = Convert.ToSingle(trackPointRow.Elevation),
Latitude = Convert.ToDouble(trackPointRow.Latitude),
Longitude = Convert.ToDouble(trackPointRow.Longitude),
Time = trackPointRow.TrackTime is DBNull ? new DateTime() : (DateTime)trackPointRow.TrackTime
};
result.Add(trackPoint);
}
return result;
}
var firstRoute = aspdb.RouteLinqs
.Where(r => r.UserId == userId && r.RouteId == routeId)
.FirstOrDefault();
if (firstRoute == null)
{
return null;
}
else
{
return new Route(routeId)
{
Name = first.SourceName,
Time = first.CreationTime ?? new DateTime(),
TrackPoints = GetTrackPointsForRoute(routeId)
};
}
If this is LINQ to SQL you can simplify it further (this won't work with LINQ to Entity Framework though):
return aspdb.RouteLinqs
.Where(r => r.UserId == userId && r.RouteId == routeId)
.Select(r => new Route(routeId)
{
Name = r.SourceName,
Time = r.CreationTime ?? new DateTime(),
TrackPoints = GetTrackPointsForRoute(routeId)
})
.FirstOrDefault();
Note: You probably can replace GetTrackPointsForRoute with a join to the child table, meaning that the entire method can be done with a single call to the database, rather than one call to get the routes, and a second call to get the points. To do this you should learn about associations and joins in LINQ to SQL.

Categories