I want to convert the linq query results to datatable so that I can assign datatable to GridView to show it on asp page.
However I am not able to convert the results to datatable, I am not getting CopyToTable() method in my code.
please advise what am I doing wrong here?
var gradeData = (from data in oAngieCtxt.prc_ShopInstanceCustomersData(Convert.ToInt32(this.ShopInstanceID), 10000, false)
.Where( row => row.RecievedPoints != "n/a" )
.GroupBy(row => new { row.Name })
.Select(g => new GroupedPoints()
{
Name = g.Key.Name,
TotalPoints = g.Sum(x => Convert.ToDouble(x.RecievedPoints) * (x.Weightage.ToString() == "0.00" ? 1 : Convert.ToDouble(x.Weightage)))
})
select data).ToList();
DataTable dt = gradeData --gradeData.CopyToTable()
Note: reference to dataextentions dll is available.
Thanks in advance
You should get DataTableExtensions.CopyToDataTable
Remove ToList().
CopyToDataTable is an IEnumerable<DataRow> extension (unfortunately).
There is a solution with custom CopyToDataTable extension method below.
var gradeData = (from data in oAngieCtxt.prc_ShopInstanceCustomersData(
Convert.ToInt32(this.ShopInstanceID), 10000, false)
.Where( row => row.RecievedPoints != "n/a" )
.GroupBy(row => new { row.Name })
.Select(g => new
{
Name = g.Key.Name,
TotalPoints = g.Sum(x => Convert.ToDouble(x.RecievedPoints)
* (x.Weightage.ToString() == "0.00" ? 1
: Convert.ToDouble(x.Weightage)))
})
select data);
var dt = gradeData.CopyToDataTable();
Edit:
Here is a more useful implementation of CopyToDataTable There is no type constraint to DataRow.
public static class DataSetLinqOperators
{
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source)
{
//you find the ObjectShredder implementation on the blog wich was linked.
return new ObjectShredder<T>().Shred(source, null, null);
}
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source,
DataTable table, LoadOption? options)
{
return new ObjectShredder<T>().Shred(source, table, options);
}
}
First declare a new DataTable and add columns, in this :
DataTable dt = new DataTable();
dt.Columns.Add("FirstName");
dt.Columns.Add("LastName");
DataRow row = null;
Now I simply iterate through the query and fill a DataTable:
foreach (var rowObj in query)
{
row = dt.NewRow();
dt.Rows.Add(rowObj.FirstName, rowObj.LastName);
}
If you want to have you own extension method then you could always do something like this:
public static DataTable ToDataTable<T>(this IQueryable items)
{
Type type = typeof(T);
var props = TypeDescriptor.GetProperties(type)
.Cast<PropertyDescriptor>()
.Where(propertyInfo => propertyInfo.PropertyType.Namespace.Equals("System"))
.Where(propertyInfo => propertyInfo.IsReadOnly == false)
.ToArray();
var table = new DataTable();
foreach (var propertyInfo in props)
{
table.Columns.Add(propertyInfo.Name, Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType);
}
foreach (var item in items)
{
table.Rows.Add(props.Select(property => property.GetValue(item)).ToArray());
}
return table;
}
You will need to reference both of these
using System.ComponentModel;
using System.Data;
Related
I use Generic and Reflection, so the main problem is add several fields. When i use this code with one field it OK but when i try somehow add new fields it doesn't work:
public static ISearchResponse<T> PartSearch<T>(ElasticClient client, string query, List<string> fieldList = null, int from = 0, int size = 1) where T : class
{
if (client == null)
throw new ArgumentNullException();
if (String.IsNullOrEmpty(query))
throw new ArgumentNullException();
ISearchResponse<T> results;
if (fieldList == null)
{
results = client.Search<T>(q =>
q.Query(q =>
q.QueryString(qs => qs.Query(query))
).From(from).Size(size)
);
}
else
{
results = client.Search<T>(q =>
q.Query(q => q
.QueryString(qs =>
{
//This place where i try to add several fields
List<Field> fildArray = new List<Field>();
foreach (var arg in fieldList)
{
var fieldString = new Field(typeof(T).GetProperty(arg));
fildArray.Add(fieldString);
}
qs.Fields(f => f.Field(fildArray));
qs.Query(query);
return qs;
})
).From(from).Size(size)
);
}
return results;
}
I created an example using Lenient() that can help you with your question:
https://github.com/hgmauri/elasticsearch-with-nest/blob/master/src/Sample.Elasticsearch.Domain/Abstractions/NestExtensions.cs
Problem was in one QueryString parameter Lenient:
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html
Fixed this by setting it on True.
I am passing a query result from the controller to a view through a Viewbag but when looping through the Viewbag result I get the error 'object' does not contain a definition for 'Ratings' even though it does show up when debugging. I am not using a model for the query so I cannot cast the list.
How do I send through the list from the query or the query results itself to the view to so I loop through and extract the data?
Controller
var AppQuery = (from ans in db.Answers
join ques in db.Questions on ans.QuestionID equals ques.QuestionID
join resp in db.Responses on ans.ResponseID equals resp.ResponseID
join sec in db.Sections on ques.SectionID equals sec.SectionID
where resp.ResponseID == ID && ques.SubSectionName != null
select new { SectionName = sec.SectionName, RatingAnswer = ans.RatingAnswer })
.GroupBy(a => a.SectionName)
.Select(a => new { SectionName = a.Key, Ratings = a.Sum(s => s.RatingAnswer) });
ViewBag.Results = AppQuery.ToList();
View
#{var Results = ViewBag.Results;}
#foreach (var item in Results)
{
var style = "active";
var condition = "";
if (item.Ratings >= 90)
{
style = "success";
condition = "Excellent";
}
else if (item.Ratings < 50)
{
style = "danger";
condition = "Critical";
}
else
{
style = "active";
condition = "Stable";
}
<tr class="#style">
<td>#item.SectionName</td>
<td>#item.Ratings</td>
<td>#condition</td>
</tr>
}
Thanks for the help!
This is the expected behaviour!. Because you set a collection of anonymous objects to your ViewBag.
You should be using a strongly typed view model.
public class RatingVm
{
public string SectionName { set;get;}
public int Ratings { set;get;}
}
and in your action method,
public ActionResult Create()
{
var results= (from ans in db.Answers
join ques in db.Questions on ans.QuestionID equals ques.QuestionID
join resp in db.Responses on ans.ResponseID equals resp.ResponseID
join sec in db.Sections on ques.SectionID equals sec.SectionID
where resp.ResponseID == ID && ques.SubSectionName != null
select new { SectionName = sec.SectionName,
RatingAnswer = ans.RatingAnswer })
.GroupBy(a => a.SectionName)
.Select(a => new RatingVm { SectionName = a.Key,
Ratings = a.Sum(s => s.RatingAnswer) }).ToList();
return View(results);
}
Now your view should be strongly typed to the type of data we are passing from our action method, which is a collection of RatingVm
#model List<RatingVm>
<h1>Ratings</h1>
#foreach(var item in Model)
{
<p>#item.SectionName</p>
<p>#item.Ratings</p>
}
Do I need to define the schema? If so, what should that look like? My searches for this seem to only turn up js solutions, I'm looking for the syntax to define it in the editortemplate.
Shared/editortemplate:
#(
Html.Kendo().DropDownList()
.Name("SearchFunction")
.DataTextField("SearchFunctionDesc")
.DataValueField("SearchFunctionCode")
.DataSource(source =>
{
source.Read(read => {
read.Action("GetSearchFunctions", "User");
});
})
.OptionLabel("--Select a Search Function--")
.AutoBind(false)
)
In the controller:
public JsonResult GetSearchFunctions([DataSourceRequest] DataSourceRequest request)
{
var searchFuncs = AdminService.GetSearchFunctions();
DataSourceResult result = searchFuncs.ToDataSourceResult(request);
return Json(result, JsonRequestBehavior.AllowGet);
}
And then my Dapper db query:
var result = new List<SearchFunction>();
using (var conn = new OracleConnection(DatabaseConnectionString))
{
conn.Open();
string query = "select FUNCTION_ID, SEARCH_FUNCTION_CD, " +
"SEARCH_FUNCTION_DESC, IS_ACTIVE " +
"from TBL_SEARCH_FUNCTIONS ";
result = conn.Query(query)
.Select(s => new SearchFunction
{
FunctionId = (int)s.FUNCTION_ID,
SearchFunctionCode = s.SEARCH_FUNCTION_CD,
SearchFunctionDesc = s.SEARCH_FUNCTION_DESC,
Active = s.IS_ACTIVE
}).ToList<SearchFunction>();
conn.Close();
return result;
}
Rewrite your controller method like this:
public JsonResult GetSearchFunctions()
{
var searchFuncs = cmsViewAdminService.GetSearchFunctions();
return Json(searchFuncs, JsonRequestBehavior.AllowGet);
}
That should simplify that method as you don't need the DataSourceRequest (as #CSharper mentioned in the comment). Kendo DropDownLists, unlike the grids, don't require the DataSourceRequest class. This way, you can call the same JsonResult from a jQuery Ajax method if you needed to do so.
You need to return a pure collection from json something that looks like that
{[
{"Id":2,"Name":"some"},
{"Id":3,"Name":"som2"}
]}
If you use ModelView and lambda then you might also try to the code below:
Assume you have a city dropdownlist retrieving data from reference table (populated to CityViewModel):
public JsonResult GetCities()
{
var dataContext = new EFDbContext();
var cities = dataContext.Cities.Select(c => new CityViewModel
{
ID = c.ID,
Name = c.Name
});
return Json(cities, JsonRequestBehavior.AllowGet);
}
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
public ActionResult PopulateFromDB(string sidx, string sord, int page, int rows)
{
var context = new NerdDinnerEntities();
var jsonData = new
{
total = 1,
page = page,
sord =sord,
records = context.Authors.Count(),
rows = (from n in context.Authors
select new
{ AuthorId = n.AuthorId ,
cell = new string[] { n.AuthorId.ToString(), n.Name.ToString(), n.Location.ToString() }
}).ToList()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
I am writting ToList or Toarray is it not working the error comes :
public ActionResult PopulateFromDB(string sidx, string sord, int page, int rows)
{
var context = new NerdDinnerEntities();
var jsonData = new
{
total = 1,
page = page,
sord =sord,
records = context.Authors.Count(),
rows = (from n in context.Authors
select new
{ AuthorId = n.AuthorId ,
cell = new string[] { n.AuthorId.ToString(), n.Name.ToString(), n.Location.ToString() }
}).ToList()
};
return Json(jsonData,JsonRequestBehavior.AllowGet);
}
From your code I assume your adding a custom property cell for display/storage purposes on the client-side. I would avoid this as your essentially coupling your API call to one particular client. I would suggest you simply return the data required & deal with it at the client-side specifically e.g.
Server
...
select new
{
Id = n.AuthorId,
Name = n.Name,
Location = n.Location
}).ToList();
...
Client
var response = ...
foreach (var author in response)
{
var cell = new string[] { author.Id.ToString(), author.Name, author.Location };
// do something with cell
}
You should try SqlFunctions.StringConvert to convert this, There is no overload for int so you should cast your number to a double or a decimal.
public ActionResult PopulateFromDB(string sidx, string sord, int page, int rows)
{
var context = new NerdDinnerEntities();
var jsonData = new
{
total = 1,
page = page,
sord =sord,
records = context.Authors.Count(),
rows = (from n in context.Authors
select new
{ AuthorId = n.AuthorId ,
cell = new string[] { SqlFunctions.StringConvert((double)n.AuthorId), n.Name, n.Location }
}).ToList()
};
return Json(jsonData,JsonRequestBehavior.AllowGet);
}
You are not using LinqToSql Classes, if you were using that your code should work, but as you mention that you are using LinqToEntity then You should use SqlFunctions.StringConvert to convert to string.
I am using ASP.NET MVC 3.
Can someone please help me clarify what's happening here:
var person = new PersonRepository().Get();
var personViewModel = new PersonViewModel();
personViewModel.InjectFrom<LoopValueInjection>(person)
.InjectFrom<CountryToLookup>(person);
I have a grid on my Index view. Each row is an instance of a CategoryViewModel. So what I do is to get a list of all the categories and then map each Category to a CategoryViewModel, and then pass this list of CategoryViewModels to the view. Hou would I do a mapping like that?
IEnumerable<Category> categoryList = categoryService.GetAll();
I thought the following would work but it doesn't:
// Mapping
IList<CategoryViewModel> viewModelList = new List<CategoryViewModel>();
viewModelList.InjectFrom(categoryList);
AFAIK value injecter doesn't support automatic collection mapping like AutoMapper but you could use a simple LINQ expression and operate on each element:
IEnumerable<Category> categoryList = categoryService.GetAll();
IList<CategoryViewModel> viewModelList = categoryList
.Select(x => new CategoryViewModel().InjectFrom(x)).Cast<CategoryViewModel>()
.ToList();
//source list
IEnumerable<string> items = new string[] { "1", "2" };
// target list
List<int> converted = new List<int>();
// inject all
converted.InjectFrom(items);
And the extension method:
public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, IEnumerable<TFrom> from) where TTo : new()
{
foreach (var source in from)
{
var target = new TTo();
target.InjectFrom(source);
to.Add(target);
}
return to;
}
ICollection<T> is the interface that got least features but a Add method.
Update
An example using more proper models:
var persons = new PersonRepository().GetAll();
var personViewModels = new List<PersonViewModel>();
personViewModels.InjectFrom(persons);
Update - Inject from different sources
public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, params IEnumerable<TFrom>[] sources) where TTo : new()
{
foreach (var from in sources)
{
foreach (var source in from)
{
var target = new TTo();
target.InjectFrom(source);
to.Add(target);
}
}
return to;
}
Usage:
var activeUsers = new PersonRepository().GetActive();
var lockedUsers = new PersonRepository().GetLocked();
var personViewModels = new List<PersonViewModel>();
personViewModels.InjectFrom(activeUsers, lockedUsers);
Use this function definition
public static object InjectCompleteFrom(this object target, object source)
{
if (target.GetType().IsGenericType &&
target.GetType().GetGenericTypeDefinition() != null &&
target.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
target.GetType().GetGenericTypeDefinition().GetInterfaces()
.Contains(typeof(IEnumerable)) &&
source.GetType().IsGenericType &&
source.GetType().GetGenericTypeDefinition() != null &&
source.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
source.GetType().GetGenericTypeDefinition().GetInterfaces()
.Contains(typeof(IEnumerable)))
{
var t = target.GetType().GetGenericArguments()[0];
var tlist = typeof(List<>).MakeGenericType(t);
var addMethod = tlist.GetMethod("Add");
foreach (var sourceItem in source as IEnumerable)
{
var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(sourceItem);
addMethod.Invoke(target, new[] { e });
}
return target;
}
else
{
return target.InjectFrom(source);
}
}
For those like me who prefer shortest notations possible
public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, ICollection<TOrig> source) where TTarget : new()
{
source.Select(r => new TTarget().InjectFrom(r))
.Cast<TTarget>().ToList().ForEach(e => target.Add(e));
return target;
}
public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, params ICollection<TOrig>[] sources) where TTarget : new()
{
sources.ToList().ForEach(s => s.ToList().Select(r => new TTarget().InjectFrom(r))
.Cast<TTarget>().ToList().ForEach(e => target.Add(e)));
return target;
}
Create a generic list mapper:
public class ValueMapper
{
public static TResult Map<TResult>(object item) where TResult : class
{
return item == null ? null : Mapper.Map<TResult>(item);
}
public static IEnumerable<TResult> MapList<TResult>(IEnumerable<object> items) where TResult : class
{
return items?.Select(i => Mapper.Map<TResult>(i));
}
}
Now you can reference the ValueMapper class wherever you want, and call both Map and MapList
var mydtos = ValueMapper.MapList<MyDto>(dtos);
var mydto = ValueMapper.Map<MyDto>(dto);