How to maintain relationships when importing data into Parse.com - mysql

I have a database that I'd like to import into Parse.com but having trouble wrapping my head around how the data should be stored in Parse and maintain relationship between this data.
Here's my current data and their relationship.
Table 1: Videos
-> VideoName
-> URL
Table 2: Performers
-> PerformerName
-> Bio
Table 3: Games
-> GameName
-> GameDetail
Table 4: Seasons
-> SeasonName
-> SeasonDetail
The relationships are:
-> Each Season can have more than one video, but each video can only be associated with one season.
-> Each video can have one or more Game
-> Each Video can have one or more Performer
-> Each Game can be associated with one or more Video
-> Each Game can be associated with one or more Performer
-> Each Performer can be in one or more Videos
-> Each Performer can be associated with one or more Game.
This means that there's many to many relationships between the data.
If someone can help me get my thinking straight on how Parse does relational databases/information I'd appreciate it. I have already read the Relational Data documentation but I'm not fully understanding how it applies to my data.
I would like to be able to export the data out of my database and import it into Parse. I am open to any suggestion on getting this data into Parse and maintaining relationships.
I need the relationships because the app is going to be pulling queries like:
-> Retrieve all videos where performer = x
-> Retrieve all games where performer = x
-> Retrieve all performers where game = x
-> Retrieve all videos where season = x
plus others as needed.
I hope someone can help. If you can provide specific examples based on the information I have above that would be excellent! :-)

Parse supports relationships in a number of ways:
Pointer (think foreign key ID)
Array of Pointers (think join table, but only when you always want to get all of them)
Relation (think join table, where you want to query for a sub-set)
What you use depends greatly on your queries. In your case I would suggest the following:
Videos:
Name (string)
URL (string)
Season (pointer)
Games (array, assuming this is a short list)
Performers (array)
Performers:
Name (string)
Bio (string, possibly with HTML or other markup)
Games:
Name (string)
Detail (string)
Performers (array)
Seasons:
Name (string)
Detail (string)
For your queries (in JavaScript since you didn't specify a language):
// setup bits
var Video = Parse.Object.extend("Video");
var Performer = Parse.Object.extend("Performer");
var Game = Parse.Object.extend("Game");
var Season = Parse.Object.extend("Season");
// All videos where performer = x
var videosForPerformerQuery = new Parse.Query(Video);
// assumes performerX has been retrieved or initialised with an ID
// equalTo in this case will find records where performerX is in the array of performers
videosForPerformerQuery.equalTo('performers', performerX);
// if you need to output the full list of performers:
videosForPerformerQuery.include('performers');
// if you need the full season object:
videosForPerformerQuery.include('season');
videosForPerformerQuery.find({
success: function(results) {
// TODO: handle array of video objects in results
},
error: function(error) {
// TODO: handle error
}
});
// All games where performer = x
var gamesForPerformerQuery = new Parse.Query(Game);
// .. identical to above ..
// All performers where game = x
var gameQuery = new Parse.Query(Game);
gameQuery.include('performers');
// gameObjectID is a string key, e.g. "xWMyZ4YEGZ"
gameQuery.get(gameObjectId, {
success: function(game) {
// TODO: game.performers array should be populated, use it
},
// .. error handler too ..
});
// All videos where season = x
var videosForSeason = new Parse.Query(Video);
videosForSeason.equalTo('season', seasonX);
// .. find same as above examples ..
Sometimes you'll want bi-directional joins. You can either manually populate an Array/Relation on each end, or use a new object to hold the relations (especially if the relation contains extra info). Example:
GamePerformers:
Game (pointer)
Performer (pointer)
Role (string)
Query it and include the Game/Performer as needed:
var GamePerformers = Parse.Object.extend("GamePerformers");
var performersForGameQuery = new Parse.Query(GamePerformers);
performersForGameQuery.equalTo('game', gameX);
performersForGameQuery.include('performer');
performersForGameQuery.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
var object = results[i];
console.log('Performer: '
+ object.get('performer').get('name')
+ ' played role: '
+ object.get('role'));
}
}
});

Related

Sorting array function over JSON loops like crazy

so I'm trying to deal with a function but it's looping like crazy and I can't figure out why.
Basically, I want to loop over a json file, retrieve every "average" value and sort it in a new array, so when I call the function ranking(countries[iso].average), it returns the position in the array.
It's actually working but the json file is way bigger, and when I console.log(rank) in the loop, it returns more than 27K messages.
ranking = (n) => {
var rank = [];
if (n) {
for (let iso in countries) {
var newvar = countries[iso].average;
rank.push(newvar);
rank.sort(function(a, b) {
return b - a;
});
}
return rank.indexOf(n) + 1
}
};
{"countries":{"US":{"name":"United States of America","ranking":"","average":13.12,"flag":"https://restcountries.eu/data/usa.svg","altNames":["US","USA"],"reports":1302,"cases":0,"deaths":299692,"recovered":23232,"lat":38,"lng":-97,"deltaCases":2,"deltaDeaths":3,"deltaRecovered":0,"casesPerOneMillion":2,"deathsPerOneMillion":903,"totalTests":22323,"testsPerOneMillion":3434,"population":345},"IN":{"name":"India","ranking":"","average":10.22,"flag":"https://restcountries.eu/data/ind.svg","altNames":["IN","Bhārat"],"reports":1016,"cases":9796992,"deaths":142222,"recovered":9290834,"lat":20,"lng":77,"deltaCases":null,"deltaDeaths":null,"deltaRecovered":646,"casesPerOneMillion":7068,"deathsPerOneMillion":103,"totalTests":151632223,"testsPerOneMillion":109402,"population":1295210000},"RU":{"name":"Russian Federation","ranking":"","average":13.21,"flag":"https://restcountries.eu/data/rus.svg","altNames":["RU","Rossiya"],"reports":1321,"cases":2597711,"deaths":45893,"recovered":2059840,"lat":60,"lng":100,"deltaCases":28585,"deltaDeaths":613,"deltaRecovered":26171,"casesPerOneMillion":17797,"deathsPerOneMillion":314,"totalTests":81564365,"testsPerOneMillion":558804,"population":146599183}}}
Thanks for any help on this
I believe that what you may be trying to do is sort by the field called average for countries in the iso. So you have some lookup called countries and there are ISOs there like I imagine: 'US'. Then Rank is an array of all these countries.
The problem I see is that you have sort happening within the for loop.
The way you explained the problem seems like 2 different steps. One retrieve the average, then AFTER that sort by the average.
If really all you want is the averages in the array: you can do like
const averages = Object.values(countries).map(country => country.average)
That single step will get you all the averages into a single array.
Then next you can sort using the same function you posted. (The key is to brake that into a second loop not a nested loop:
averages.sort((a, b) => b - a)
// now sorted
But in case you wanted to keep the rest of the data you can do that pretty easily as well:
Something more like:
const countriesSortedByAverage = Object.values(countries).sort((a, b) => b.average - a.average)
If you really need the ISO you can also do the same with Object.entries but it might be even easier to provide the iso inside the country Object.
To determine the rank for all countries you can easily add that to (if you wanted) and have that be the principal country Object:
const RANKED_LIST_OF_COUNT = countriesSortedByAverage.map((countryObj, rank) => ({ ...countryObj, rank }))
If you want to further restore it to the CountriesByISO object:
const COUNTRIES_BY_ISO_WITH_RANK = Object.assign({}, ...RANKED_LIST_OF_COUNT.map(country => ({ [country.ISO]: country}))
)

CollectionView.reloadData() outputs cells in incorrect order

I am working on an app that requires a sync to the server after logging in to get all the activities the user has created and saved to the server. Currently, when the user logs in a getActivity() function that makes an API request and returns a response which is then handled.
Say the user has 4 activities saved on the server in this order (The order is determined by the time of the activity being created / saved) ;
Test
Bob
cvb
Testing
looking at the JSONHandler.getActivityResponse , it appears as though the the results are in the correct order. If the request was successful, on the home page where these activities are to be displayed, I currently loop through them like so;
WebAPIHandler.shared.getActivityRequest(completion:
{
success, results in DispatchQueue.main.async {
if(success)
{
for _ in (results)!
{
guard let managedObjectContext = self.managedObjectContext else { return }
let activity = Activity(context: managedObjectContext)
activity.name = results![WebAPIHandler.shared.idCount].name
print("activity name is - \(activity.name)")
WebAPIHandler.shared.idCount += 1
}
}
And the print within the for loop is also outputting in the expected order;
activity name is - Optional("Test")
activity name is - Optional("Bob")
activity name is - Optional("cvb")
activity name is - Optional("Testing")
The CollectionView does then insert new cells, but it seemingly in the wrong order. I'm using a carousel layout on the home page, and the 'cvb' object for example is appearing first in the list, and 'bob' is third in the list. I am using the following
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?)
{
switch (type)
{
case .insert:
if var indexPath = newIndexPath
{
// var itemCount = 0
// var arrayWithIndexPaths: [IndexPath] = []
//
// for _ in 0..<(WebAPIHandler.shared.idCount)
// {
// itemCount += 1
//
// arrayWithIndexPaths.append(IndexPath(item: itemCount - 1, section: 0))
// print("itemCount = \(itemCount)")
// }
print("Insert object")
// walkThroughCollectionView.insertItems(at: arrayWithIndexPaths)
walkThroughCollectionView.reloadData()
}
You can see why I've tried to use collectionView.insertItems() but that would cause an error stating:
Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (4) must be equal to the number of items contained in that section before the update (4), plus or minus the number of items inserted or deleted from that section (4 inserted, 0 deleted)
I saw a lot of other answers mentioning how reloadData() would fix the issue, but I'm real stuck at this point. I've been using swift for several months now, and this has been the first time I'm truly at a loss. What I also realised is that the order displayed in the carousel is also different to a separate viewController which is passed the same data. I just have no idea why the results return in the correct order, but are then displayed in an incorrect order. Is there a way to sort data in the collectionView after calling reloadData() or am I looking at this from the wrong angle?
Any help would be much appreciated, cheers!
The order of the collection view is specified by the sort descriptor(s) of the fetched results controller.
Usually the workflow of inserting a new NSManagedObject is
Insert the new object into the managed object context.
Save the context. This calls the delegate methods controllerWillChangeContent, controller(:didChange:at: etc.
In controller(:didChange:at: insert the cell into the collection view with insertItems(at:, nothing else. Do not call reloadData() in this method.

How to deal with Parsing Object from Join Query

I'm currently working on my first API with the Perfect framework. It's been a while since I made an API myself so I must admit my SQL and API logic is a little rusty.
I'm using a MySQL database for my implementation.
For sake of example I'll explain my database structure below;
I have a table which resembles an Object, let's call this Table A. Table A has a Varchar based id as primary key.
There are 2 other tables let's call them Table B and Table C. Table A has a one to many relation to both Table B and C. Where the id of table A is the foreign key.
What I'm trying to do is obtain everything with one query and cast it to an object in my backend.
By using outer joins I'm making the call to retrieve all the required data.
SELECT control.id, control.type, control.description, control.address, control.city, control.created, control.updated, control.latitude, control.longitude, images.id AS image_id, images.image, images.description AS image_description, updates.id AS update_id, updates.still_present, updates.created_at AS update_created
FROM Control control left outer join control_images images
ON control.id=images.control_id
left outer join Control_Updates updates
ON control.id=updates.control_id
Now is my question what would be the best way to store this data in an object that holds an array of updates and an array of images.
Before writing the join query I only attempted to get the values from Table A I used the following code to cast the results to my desired object.
let result = mysql.storeResults()
let checkResult = self.checkResult(result: result, response: response)
response = checkResult.response
var controls: [Control] = []
while let row = result?.next() {
let type = Types(rawValue: row[1].unwrap)!
let control = Control(id: row[0].unwrap, type: type, description: row[2].unwrap, address: row[3].unwrap, city: row[4].unwrap, latitude: Double(row[7].unwrap).unwrap, longitude: Double(row[8].unwrap).unwrap)
controls.append(control)
}
obviously this will just return duplicate objects apart from the images and updates of course.
I'm wondering if this is the best way to do it or if I should call a new query in the while loop
The best way to resolve this issue, by still only using one query and one loop is by using 'hashmaps'. I'm not familiar with Perfect framework, but in PHP it would look something like:
// Get results from the db:
$results = $db->execute($query, $params);
// Define map for controls:
$map = [];
// Loop over results/rows
foreach($results as $row){
// Get unique identifier for the Control model:
$controlId = $row['id'];
// Check if control is NOT already in map:
if(!isset($map[$controlId]){
// Add control to map:
$control = [
'id' => $controlId,
'description' => $row['description'],
'images' => []
// other fields
];
// Add control to map:
$map[$controlId] = $control;
}
else{
// Control exists, retrieve it from the map:
$control = $map[$controlId];
}
// Retrieve unique identifier of the image:
$imageId = $row['image_id'];
// Same tactic with hasmap, check if control already has the image, if not add it
if(!isset($control['images'][$imageId]){
// add the image to the hashmap:
}
else{
// Image is already added, the content from the 'update' data is not added yet, handle that part (also with a hash map)
}
}
Hope that helps you figure it out in Perfect framework

LINQ variable to list of string without using column names?

In an C# ASP.Net MVC project, I'm trying to make a List<string> from a LINQ variable.
Now this might be a pretty basic thing, but I just cannot get that to work without using the actual column names for the data in that variable. The thing is that in the interests of trying to make the program as dynamic as possible, I'm leaving it up to a stored procedure to get the data out. There can be any amount of any which way named columns depending on where the data is fetched from. All I care about is taking all of their values into a List<string>, so that I can compare user-input values with them in program.
Pointing to the columns by their names in the code means I'd have to make dozens of overloaded methods that all just basically do the same thing. Below is false non-functioning code. But it should open up the idea of what I mean.
// call for stored procedure
var courses = db.spFetchCourseInformation().ToList();
// if the data fails a check on a single row, it will not pass the check
bool passed = true;
foreach (var i in courses)
{
// each row should be cast into a list of string, which can then be validated
// on a row-by-row basis
List courseRow = new List();
courseRow = courses[i]; // yes, obviously this is wrong syntax
int matches = 0;
foreach (string k in courseRow)
{
if (validator.checkMatch(courseRow[k].ToString()))
{
matches++;
}
}
if (matches == 0)
{
passed = false;
break;
}
}
Now below is an example of how I currently have to do it because I need to use the names for the columns
for (int i = 0; i < courses.Count; i++)
{
int matches = 0;
if (validator.checkMatch(courses[i].Name))
matches++;
if (validator.checkMatch(courses[i].RandomOtherColumn))
matches++;
if (validator.checkMatch(courses[i].RandomThirdColumn))
matches++;
if (validator.checkMatch(courses[i].RandomFourthColumn))
matches++;
/* etc...
* etc...
* you get the point
* and one of these for each and every possible variation from the stored procedure, NOT good practice
* */
Thanks for help!
I'm not 100% sure what problem you are trying to solve (matching user data to a particular record in the DB?), but I'm pretty sure you're going about this in slightly the wrong fashion by putting the data in a List. I
t should be possible to get your user input in an IDictionary with the key being used for the column name, and the object as the input data field.
Then when you get the data from the SP, you can get the data back in a DataReader (a la http://msmvps.com/blogs/deborahk/archive/2009/07/09/dal-access-a-datareader-using-a-stored-procedure.aspx).
DataReaders are indexed on column name, so if you run through the keys in the input data IDictionary, you can check the DataReader to see if it has matching data.
using (SqlDataReader reader = Dac.ExecuteDataReader("CustomerRetrieveAll", null))
{
while (reader.Read())
{
foreach(var key in userInputDictionary.AllKeys)
{
var data = reader[key];
if (data != userInputDictionary[key]) continue;
}
}
}
Still not sure about the problem you are solving but, I hope this helps!
A little creative reflection should do the trick.
var courses = db.spFetchCourseInformation()
var values = courses.SelectMany(c => c.GetType().GetProperties() // gets the properties for your object
.Select(property => property.GetValue(c, null))); // gets the value of each property
List<string> stringValues = new List<string>(
values.Select(v => v == null ? string.Empty : v.ToString()) // some of those values will likely be null
.Distinct()); // remove duplicates

Explicit construction of entity type '###' in query is not allowed.

Using Linq commands and Linq To SQL datacontext, Im trying to instance an Entity called "Produccion" from my datacontext in this way:
Demo.View.Data.PRODUCCION pocoProduccion =
(
from m in db.MEDICOXPROMOTORs
join a in db.ATENCIONs on m.cmp equals a.cmp
join e in db.EXAMENXATENCIONs on a.numeroatencion equals e.numeroatencion
join c in db.CITAs on e.numerocita equals c.numerocita
where e.codigo == codigoExamenxAtencion
select new Demo.View.Data.PRODUCCION
{
cmp = a.cmp,
bonificacion = comi,
valorventa = precioEstudio,
codigoestudio = lblCodigoEstudio.Content.ToString(),
codigopaciente = Convert.ToInt32(lblCodigoPaciente.Content.ToString()),
codigoproduccion = Convert.ToInt32(lblNroInforme.Content.ToString()),
codigopromotor = m.codigopromotor,
fecha = Convert.ToDateTime(DateTime.Today.ToShortDateString()),
numeroinforme = Convert.ToInt32(lblNroInforme.Content.ToString()),
revisado = false,
codigozona = (c.codigozona.Value == null ? Convert.ToInt32(c.codigozona) : 0),
codigoclinica = Convert.ToInt32(c.codigoclinica),
codigoclase = e.codigoclase,
}
).FirstOrDefault();
While executing the above code, I'm getting the following error that the stack trace is included:
System.NotSupportedException was caught
Message="The explicit construction of the entity type 'Demo.View.Data.PRODUCCION' in a query is not allowed."
Source="System.Data.Linq"
StackTrace:
en System.Data.Linq.SqlClient.QueryConverter.VisitMemberInit(MemberInitExpression init)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.VisitSelect(Expression sequence, LambdaExpression selector)
en System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.VisitFirst(Expression sequence, LambdaExpression lambda, Boolean isFirst)
en System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.ConvertOuter(Expression node)
en System.Data.Linq.SqlClient.SqlProvider.BuildQuery(Expression query, SqlNodeAnnotations annotations)
en System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query)
en System.Data.Linq.DataQuery`1.System.Linq.IQueryProvider.Execute[S](Expression expression)
en System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
en Demo.View.InformeMedico.realizarProduccionInforme(Int32 codigoExamenxAtencion, Double precioEstudio, Int32 comi) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 602
en Demo.View.InformeMedico.UpdateEstadoEstudio(Int32 codigo, Char state) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 591
en Demo.View.InformeMedico.btnGuardar_Click(Object sender, RoutedEventArgs e) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 683
InnerException:
Is that now allowed in LINQ2SQL?
Entities can be created outside of queries and inserted into the data store using a DataContext. You can then retrieve them using queries. However, you can't create entities as part of a query.
I am finding this limitation to be very annoying, and going against the common trend of not using SELECT * in queries.
Still with c# anonymous types there is a workaround, by fetching the objects into an anonymous type, and then copy it over into the correct type.
For example:
var q = from emp in employees where emp.ID !=0
select new {Name = emp.First + " " + emp.Last, EmployeeId = emp.ID }
var r = q.ToList();
List<User> users = new List<User>(r.Select(new User
{
Name = r.Name,
EmployeeId = r.EmployeeId
}));
And in the case when we deal with a single value (as in the situation described in the question) it is even easier, and we just need to copy directly the values:
var q = from emp in employees where emp.ID !=0
select new { Name = emp.First + " " + emp.Last, EmployeeId = emp.ID }
var r = q.FirstOrDefault();
User user = new User { Name = r.Name, EmployeeId = r.ID };
If the name of the properties match the database columns we can do it even simpler in the query, by doing select
var q = from emp in employees where emp.ID !=0
select new { emp.First, emp.Last, emp.ID }
One might go ahead and write a lambda expression that can copy automatically based on the property name, without needing to specify the values explictly.
Here's another workaround:
Make a class that derives from your LINQ to SQL class. I'm assuming that the L2S class that you want to return is Order:
internal class OrderView : Order { }
Now write the query this way:
var query = from o in db.Order
select new OrderView // instead of Order
{
OrderID = o.OrderID,
OrderDate = o.OrderDate,
// etc.
};
Cast the result back into Order, like this:
return query.Cast<Order>().ToList(); // or .FirstOrDefault()
(or use something more sensible, like BLToolkit / LINQ to DB)
Note: I haven't tested to see if tracking works or not; it works to retrieve data, which is what I needed.
I have found that if you do a .ToList() on the query before trying to contruct new objects it works
I just ran into the same issue.
I found a very easy solution.
var a = att as Attachment;
Func<Culture, AttachmentCulture> make =
c => new AttachmentCulture { Culture = c };
var culs = from c in dc.Cultures
let ac = c.AttachmentCultures.SingleOrDefault(
x => x.Attachment == a)
select ac == null ? make(c) : ac;
return culs;
I construct an anonymous type, use IEnumerable (which preserves deferred execution), and then re-consruct the datacontext object. Both Employee and Manager are datacontext objects:
var q = dc.Employees.Where(p => p.IsManager == 1)
.Select(p => new { Id = p.Id, Name = p.Name })
.AsEnumerable()
.Select(item => new Manager() { Id = item.Id, Name = item.Name });
Within the book "70-515 Web Applications Development with Microsoft .NET Framework 4 - Self paced training kit", page 638 has the following example to output results to a strongly typed object:
IEnumerable<User> users = from emp in employees where emp.ID !=0
select new User
{
Name = emp.First + " " + emp.Last,
EmployeeId = emp.ID
}
Mark Pecks advice appears to contradict this book - however, for me this example still displays the above error as well, leaving me somewhat confused. Is this linked to version differences? Any suggestions welcome.
I found another workaround for the problem that even lets you retain your result as IQueryale, so it doesn't actually execute the query until you want it to be executed (like it would with the ToList() method).
So linq doesn't allow you to create an entity as a part of query? You can shift that task to the database itself and create a function that will grab the data you want. After you import the function to your data context, you just need to set the result type to the one you want.
I found out about this when I had to write a piece of code that would produce a IQueryable<T> in which the items don't actually exist in the table containing T.
pbz posted a work around by creating a View class inherited from an entity class that you could be working with. I'm working with a dbml model of a table that has > 200 columns. When I try and return the whole table I get "Root Element missing" errors. I couldn't find anyone who wanted to deal with my particular issue so I was looking at rewriting my entire approach. Just creating a view class for the entitiy class worked in my case.
As pbz suggests : Create a view class that inherits from your entity class. For me this is tbCamp so :
internal class tbCampView : tbCamp
{
}
Then use the view class in your query :
using (var dc = ConnectionClass.Connect(Dev))
{
var camps = dc.tbCamps.Select(s => new tbCampView
{
active = s.active,
idCamp = s.idCamp,
campName = s.campName
});
SmartTableViewer(camps, dg1);
}
private void SmartTableViewer<T>(IEnumerable<T> allRecords)
{
// Build sorted rows back into new table
var table = new DataTable();
// Create columns based on type
if (allRecords is IEnumerable<tbCamp> tbCampRecords)
{
// Get the columns you want
table.Columns.Add("idCamp");
table.Columns.Add("campName");
foreach (var record in tbCampRecords)
{
// Make a new row
var r = table.NewRow();
// Add the contents to each column of the row
r["idCamp"] = record.idCamp;
r["campName"] = record.campName;
// Add the row to the table.
table.Rows.Add(r);
}
}
else
{
MessageBox.Show("Unhandled type. Add support for new data type in SmartTableViewer()");
return;
}
// Update table in grid
dg1.DataSource = table.DefaultView;
}
Here is what happens when you try and create an entity class object in the query.
I didn't want to have to use an anonymous type if I could help it because I wanted the type to be tbCamp. Since tbCampView is of type tbCamp the is operator works well. see Brian Hasden's answer Passing a generic List<> in C#
I'm surprised this is even an issue but with larger tables I run into this error so I thought I would just show it here :
When trying to read this table into memory I get the following error. There are < 2000 rows but the columns are > 200 for each. I don't know if that is an issue or not.
If I just want a few columns I need to create a custom class and handle that which isn't that big of a pain. With the approach pbz provided I don't have to worry about it.
Here is the entire project in case it helps someone.
public partial class Form1 : Form
{
private const bool Dev = true;
public Form1()
{
InitializeComponent();
}
private void btnGetAllCamps_Click(object sender, EventArgs e)
{
using (var dc = ConnectionClass.Connect(Dev))
{
IQueryable<tbCampView> camps = dc.tbCamps.Select(s => new tbCampView
{
// Project columns as needed.
active = s.active,
idCamp = s.idCamp,
campName = s.campName
});
// pass in as a
SmartTableViewer(camps);
}
}
private void SmartTableViewer<T>(IEnumerable<T> allRecords)
{
// Build sorted rows back into new table
var table = new DataTable();
// Create columns based on type
if (allRecords is IEnumerable<tbCamp> tbCampRecords)
{
// Get the columns you want
table.Columns.Add("idCamp");
table.Columns.Add("campName");
foreach (var record in tbCampRecords)
{
//var newRecord = record;
// Make a new row
var r = table.NewRow();
// Add the contents to each column of the row
r["idCamp"] = record.idCamp;
r["campName"] = record.campName;
// Add the row to the table.
table.Rows.Add(r);
}
}
else
{
MessageBox.Show("Unhandled type. Add support for new data type in SmartTableViewer()");
return;
}
// Update table in grid
dg1.DataSource = table.DefaultView;
}
internal class tbCampView : tbCamp
{
}
}