trevor1940
asked on
C#: Creating child objects
Hi
I've got a PostgreSQL database that I've built a ADO.NET Entity Data Model attached
Basic Flow line is
fetch id and Lat/Long from pointonsurface table from this build URL
fetch data from geonames api Deserialize to a JSON object from this
populate geonames_countrySubdivision and countrySubdivision_code database tables
I thought I needed to create a geonames_countrySubdivision object then added countrySubdivision_code object(s) to it but
Here is my code
CountrySubdivision.cs
I've got a PostgreSQL database that I've built a ADO.NET Entity Data Model attached
Basic Flow line is
fetch id and Lat/Long from pointonsurface table from this build URL
fetch data from geonames api Deserialize to a JSON object from this
populate geonames_countrySubdivision and countrySubdivision_code database tables
I thought I needed to create a geonames_countrySubdivision object then added countrySubdivision_code object(s) to it but
Error CS1061 'geonames_countrysubdivision' does not contain a definition for 'Add' and no accessible extension method 'Add' ..
Can anyone see what I'm doing wrong?Here is my code
using GeoNameToPostgreSQL;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using static GetGeonamesData.CountrySubdivision;
namespace GetGeonamesData
{
class Program
{
static void Main(string[] args)
{
using (var contex = gazetteerEntities())
{
var PointOnSurfaces = contex.pointonsurfaces.Where(p => p.source_name == "os_parish_region_wgs84").Take(10);
if (PointOnSurfaces == null)
{
Console.WriteLine("Boo Who");
}
else
{
foreach (var PointOnSurface in PointOnSurfaces)
{
var POS_ID = PointOnSurface.id;
var db_lat = PointOnSurface.db_lat;
var db_Long = PointOnSurface.db_long;
Console.WriteLine("ID:{0}, Lat: {1}, Long: {2}", POS_ID, db_lat, db_Long);
string URL = "http://api.geonames.org/countrySubdivisionJSON?lat=" + db_lat + "&lng=" + db_Long + "&username=FAKE&level=5";
WebClient client = new WebClient();
string myJSON = client.DownloadString(URL);
//File.WriteAllText(JSONfile, myJSON);
// List<> CountrySubdivision = new List<countrySubdivision>();
Rootobject CountrySubdivision = JsonConvert.DeserializeObject<Rootobject>(myJSON);
string countryName = CountrySubdivision.countryName;
string iso_3166_alpha2 = CountrySubdivision.countryCode;
string adminName1 = CountrySubdivision.adminName1;
string adminCode1 = CountrySubdivision.adminCode1;
string adminName2 = CountrySubdivision.adminName2;
string adminCode2 = CountrySubdivision.adminCode2;
string adminName3 = CountrySubdivision.adminName3;
string adminCode3 = CountrySubdivision.adminCode3;
string adminName4 = CountrySubdivision.adminName4;
string adminCode4 = CountrySubdivision.adminCode4;
string adminName5 = CountrySubdivision.adminName5;
string adminCode5 = CountrySubdivision.adminCode5;
string subdivision_code = iso_3166_alpha2 + "-" + CountrySubdivision.codes[CountrySubdivision.codes.Length - 1].code;
Console.WriteLine(CountrySubdivision.countryName);
Console.WriteLine("countryName: {0}", countryName);
Console.WriteLine("iso_3166_alpha2: {0}", iso_3166_alpha2);
Console.WriteLine("adminName1: {0}", adminName1);
Console.WriteLine("adminCode1: {0}", adminCode1);
Console.WriteLine("adminName2: {0}", adminName2);
Console.WriteLine("adminCode2: {0}", adminCode2);
Console.WriteLine("adminName3: {0}", adminName3);
Console.WriteLine("adminCode3: {0}", adminCode3);
Console.WriteLine("adminName4: {0}", adminName4);
Console.WriteLine("adminCode4: {0}", adminCode4);
Console.WriteLine("subdivision_code: {0}", subdivision_code);
// ToDo Update the database
var NewGeonamesCountrySubdivision = new geonames_countrysubdivision
{
pointonsurface_id = POS_ID,
countryname = countryName,
countrycode = iso_3166_alpha2,
admincode1 = adminCode1,
adminname1 = adminName1,
admincode2 = adminCode2,
adminname2 = adminName2,
admincode3 = adminCode3,
adminname3 = adminName3,
admincode4 = adminCode4,
adminname4 = adminName4,
admincode5 = adminCode5,
adminname5 = adminName5,
distance = CountrySubdivision.distance,
subdivision_code = subdivision_code
};
foreach (var item in CountrySubdivision.codes)
{
var NewCountrySubdivisionCode = new countrysubdivision_code
{
code = item.code,
type = item.type,
level = item.level
};
NewGeonamesCountrySubdivision.Add
}
contex.SaveChanges();
}
}
}
Console.ReadLine();
}
}
}
CountrySubdivision.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GetGeonamesData
{
class CountrySubdivision
{
public class Rootobject
{
public string adminCode2 { get; set; }
public Code[] codes { get; set; }
public string adminCode3 { get; set; }
public string adminName4 { get; set; }
public string adminName3 { get; set; }
public string adminCode1 { get; set; }
public string adminName2 { get; set; }
public int distance { get; set; }
public string countryCode { get; set; }
public string countryName { get; set; }
public string adminName1 { get; set; }
public string adminCode4 { get; set; }
public string adminName5 { get; set; }
public string adminCode5 { get; set; }
}
public class Code
{
public string code { get; set; }
public string level { get; set; }
public string type { get; set; }
}
}
}
ASKER
database-first
Here is how I created the 2 database tables
Here is how I created the 2 database tables
CREATE TABLE geonames_countrySubdivision
(
id SERIAL PRIMARY KEY,
countryCode varchar,
countryName varchar,
adminName1 varchar,
adminCode1 varchar,
adminName2 varchar,
adminCode2 varchar,
adminName3 varchar,
adminCode3 varchar,
adminName4 varchar,
adminCode4 varchar,
adminName5 varchar,
adminCode5 varchar,
distance integer,
pointonsurface_id integer,
subdivision_code varchar,
);
CREATE TABLE countrySubdivision_code
(
id SERIAL PRIMARY KEY,
code varchar,
level varchar,
type varchar,
CONSTRAINT fk_geonames_countrySubdivision
FOREIGN KEY(id)
REFERENCES geonames_countrySubdivision(id)
);
- Why are all columns nullable?
- geonames_countrySubdivisio n(countryN ame) seems redundant. The geonames_countrySubdivisio n table should point to a contry tables for that.
- geonames_countrySubdivisio n(distance ) is the distance to what?
- countrySubdivision_code(co de) or countrySubdivision_code(co de, type) look like they must be unique.
- Why adminXXX1 to adminXXX5? Looks like a repeating group.
- geonames_countrySubdivisio n.subdivis ion_code and countrySubdivision_code.id seem to be redundant:
For the missing Add() method. You're using an array for the codes. Switch to a List<Code>, then you'll have an Add() method.
- geonames_countrySubdivisio
- geonames_countrySubdivisio
- countrySubdivision_code(co
- Why adminXXX1 to adminXXX5? Looks like a repeating group.
- geonames_countrySubdivisio
CREATE TABLE geonames_countrySubdivision
(
id SERIAL PRIMARY KEY,
pointonsurface_id integer,
subdivision_code varchar
);
CREATE TABLE countrySubdivision_code
(
id SERIAL PRIMARY KEY,
code varchar,
level varchar,
type varchar,
CONSTRAINT fk_geonames_countrySubdivision
FOREIGN KEY(id) REFERENCES geonames_countrySubdivision(id)
);
For the missing Add() method. You're using an array for the codes. Switch to a List<Code>, then you'll have an Add() method.
ASKER
To answer your question bellow is a sample from the geonames.org api the docs suggested there might be 6 admin levels hence adminName5 in my code
depending on the location of your point will return different admin levels this is why they are nullable
depending on the location of your point will return different admin levels this is why they are nullable
<geonames>
<countrySubdivision>
<countryCode>GB</countryCode>
<countryName>United Kingdom</countryName>
<adminCode1>ENG</adminCode1>
<adminName1>England</adminName1>
<code level="1" type="ISO3166-2">ENG</code>
<adminCode2>C3</adminCode2>
<adminName2>Cambridgeshire</adminName2>
<code level="2" type="ISO3166-2">CAM</code>
<adminCode3>12UE</adminCode3>
<adminName3>Huntingdonshire</adminName3>
<adminCode4>12UE085</adminCode4>
<adminName4>Yelling</adminName4>
<distance>0</distance>
</countrySubdivision>
</geonames>
Do you mean something like this? Which errors
For the missing add method. Your using an array for the codes. Switch to a List<Code>, then you'll have an Add method.
List<Rootobject> CountrySubdivision = new List<Rootobject>();
CountrySubdivision = JsonConvert.DeserializeObject<Rootobject>(myJSON);
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
From the given XML, I think the data model should look like this, as there are three entities:
countrySubdivisions ( countryCode, countryName, distance )
PK ( countryCode )
admins ( countryCode, number, code, name )
PK ( countryCode, number )
FK ( countryCode -> countrySubdivision.countryCode )
codes ( countryCode, level, type, value )
PK ( countryCode, level )
FK ( countryCode -> countrySubdivision.countryCode )
ASKER
I've updated CountrySubdivision.cs as suggested I still don't have access to "add" or "code" here
foreach (var item in CountrySubdivision.codes)
{
var NewCountrySubdivisionCode = new countrysubdivision_code
{
code = item.code,
type = item.type,
level = item.level
};
NewGeonamesCountrySubdivision. // no add
}
ASKER
Not certain your DB structure will work in this application
Is this the reason why I can't add to child table?
Is this the reason why I can't add to child table?
NewGeonamesCountrySubdivision. // no add
Requires a list or the DBContext.
ASKER
I'm inside
So I'm not certain what you mean
using (var contex = new gazetteerEntities())
{
...........
foreach (var item in CountrySubdivision.codes)
{
var NewCountrySubdivisionCode = new countrysubdivision_code {
code = item.code,
type = item.type,
level = item.level
};
NewGeonamesCountrySubdivision. // no add
}
}
So I'm not certain what you mean
ASKER
HI
I believe I've resolved the problem by adding a column to hold the FOREIGN KEY so the new child table looks like this
and the data model looks like
I can then do this to create the parent and child
I believe I've resolved the problem by adding a column to hold the FOREIGN KEY so the new child table looks like this
CREATE TABLE geonames_countrysubdivision_code
(
id SERIAL PRIMARY KEY,
gcs_id integer,
code varchar,
level varchar,
type varchar,
CONSTRAINT fk_geonames_countrysubdivision
FOREIGN KEY(gcs_id)
REFERENCES geonames_countrysubdivision(id)
);
and the data model looks like
I can then do this to create the parent and child
// ToDo Update the database
var NewGeonamesCountrySubdivision = new geonames_countrysubdivision
{
pointonsurface_id = POS_ID,
countryname = countryName,
countrycode = iso_3166_alpha2,
admincode1 = adminCode1,
adminname1 = adminName1,
admincode2 = adminCode2,
adminname2 = adminName2,
admincode3 = adminCode3,
adminname3 = adminName3,
admincode4 = adminCode4,
adminname4 = adminName4,
admincode5 = adminCode5,
adminname5 = adminName5,
distance = CountrySubdivision.distance,
subdivision_code = subdivision_code
};
foreach (var item in CountrySubdivision.codes)
{
var NewCountrySubdivisionCode = new geonames_countrysubdivision_code
{
code = item.code,
type = item.type,
level = item.level
};
NewGeonamesCountrySubdivision.geonames_countrysubdivision_code.Add(NewCountrySubdivisionCode);
}
contex.geonames_countrysubdivision.Add(NewGeonamesCountrySubdivision);
contex.SaveChanges();
That's why I've asked you about the model and the cardinality.
But the structure does still look a bit off. Are you sure that the hierarchies have the correct order?
But the structure does still look a bit off. Are you sure that the hierarchies have the correct order?
ASKER
In what way do you think they are "a bit off"?
The main geonames_countrysubdivision table and the child geonames_countrysubdivision_code table are derived from the sample from the geonames.org api I showed before
The main geonames_countrysubdivision table and the child geonames_countrysubdivision_code table are derived from the sample from the geonames.org api I showed before
I don't have read that documentation, but the semantics your model make me think the the direction of the relationships is inverse.
ASKER
OK I get what you mean now Hierarchical logic would be something like this
For the UK it would be adminName4 parts of the US adminName3 where as Thailand adminName1
Example
I'm going to have to think about this more
Sorry if going off topic
countryCode => GB countryName => United Kingdom
adminName1 => England adminCode1 => ENG code => ENG level =>1
adminName2 => Cambridgeshire adminCode2 => C3 code => CAM level =>2
adminName3 => Huntingdonshire adminCode3 => 12UE
adminName4 ect
With the pointonsurface_id inserted at whatever lowest admin level (Highest adminName number) is returned from the apiFor the UK it would be adminName4 parts of the US adminName3 where as Thailand adminName1
Example
<geonames>
<countrySubdivision>
<countryCode>TH</countryCode>
<countryName>Thailand</countryName>
<adminCode1>63</adminCode1>
<adminName1>Krabi</adminName1>
<code level="1" type="ISO3166-2">81</code>
<distance>0</distance>
</countrySubdivision>
</geonames>
I'm guessing I can do that with a Look up table (admin table name, level ID, pointonsurface_id )I'm going to have to think about this more
Sorry if going off topic
No problemo.
ASKER
Hi
I'm undecided whether to simple inverse the two tables so that the codes table becomes the parent of admin or have multiple tables
In both options:
The C# thing I don't quite understand is how the EF handles unique parents
The iso-3166 codes are unique so if this was the parent table an entry for the UK would be created once
In pure SQL before creating an entry for England you would need to search for the primary key for the UK then use it as foreign key
In C# you'd create the UK object & add an England Object to it before saving changes
What happens when the UK entry is already stored in the database?
I'm undecided whether to simple inverse the two tables so that the codes table becomes the parent of admin or have multiple tables
In both options:
The C# thing I don't quite understand is how the EF handles unique parents
The iso-3166 codes are unique so if this was the parent table an entry for the UK would be created once
In pure SQL before creating an entry for England you would need to search for the primary key for the UK then use it as foreign key
In C# you'd create the UK object & add an England Object to it before saving changes
What happens when the UK entry is already stored in the database?
ASKER
Hi
I've been playing in order to simplify & test I created a mock up bellow
What I've discovered you need to test if parent (Country) exists before creating a new 1 if not you can create it's child (Admin_1) & grand children (Admin2) etc If it dose exist the code gets complicated very quickly with testing at each stage
Is there a way to avoid the nested 'if/else' statements
I've been playing in order to simplify & test I created a mock up bellow
What I've discovered you need to test if parent (Country) exists before creating a new 1 if not you can create it's child (Admin_1) & grand children (Admin2) etc If it dose exist the code gets complicated very quickly with testing at each stage
Is there a way to avoid the nested 'if/else' statements
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EFchildParent;
namespace TestInsert
{
class Program
{
static void Main(string[] args)
{
var Places = new Dictionary<string, string>();
Places.Add("TRE", "Trevorshire" );
Places.Add("KYL", "Kylieshire" );
foreach(var county in Places)
{
Console.WriteLine(county.Key + " " + county.Value);
using (var contex = new EFchildParentEntities())
{
var Results = contex.Countrys.SingleOrDefault(c => c.countryCode == "GB");
if (Results == null)
{
var NewCountry = new EFchildParent.Country
{
countryCode = "GB",
countryName = "United Kingdom"
};
var NewAdmin1 = new EFchildParent.Admin_1
{
countryCode= "GB",
adminCode1 = county.Key,
adminName1 = county.Value,
};
NewCountry.Admin_1.Add(NewAdmin1);
// create next child admin2 add to Admin1 etc.
contex.Countrys.Add(NewCountry);
}
else
{
var results = contex.Admin_1.SingleOrDefault(a => a.adminCode1 == county.Key);
if (results == null)
{
var NewAdmin1 = new EFchildParent.Admin_1
{
countryCode = "GB",
adminCode1 = county.Key,
adminName1 = county.Value,
};
// create next child admin2 add to Admin1
// create next child admin3 add to Admin2
contex.Admin_1.Add(NewAdmin1);
}
else
{
// Test if Admin_2 exist in Database
Console.WriteLine("create new child admin2 + admin3 etc");
// else Test if Admin_3 exist in Database
// if Admin_3 exist create admin4
// else create next child admin3 + admin4
}
}
contex.SaveChanges();
}
}
Console.WriteLine("Finished");
Console.ReadLine();
}
}
}
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Do you know of an online tutorial that I could work through?
I clearly have a lot to learn
I clearly have a lot to learn
If database-first, how does your data model look like? What are its entities? Also explain the cardinality 0..1 of your subdivions. Seems strange to me.