Mitä on Geneerinen ohjelmointi (osa4)?
Tässä blogissani kuvaan, miten luodaan Json ”sanoma” esitellyn metadatan avulla. Lisäksi perehdytään sen talletukseen Sql serveriin.
Seuraavassa vaiheessa luodaan dataluokan tietojen tarkastus ja käsittely. Luokka luo rakenteensa metadatan ohjeiden mukaan.
public class DataCollection
{
[JsonProperty(PropertyName = ”n”, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
private String _collectionName;
[JsonProperty(PropertyName = ”m”, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
private String _metadata;
[JsonProperty(PropertyName = ”d”, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
private Dictionary<String, String> _data = new Dictionary<String, String>();
private Dictionary<String, IVariable> _variables = new Dictionary<String, IVariable>();
public DataCollection(String metadatax)
{
_metadata = metadatax;
PurgeMetadata();
}
public void Validate()
{
PurgeMetadata();
Dictionary<String, String> updates = new Dictionary<string, string>();
foreach (var item in _data)
{
String modified = _variables[item.Key].Validate(item.Value);
if (item.Value != modified)
{
updates.Add(item.Key, modified);
}
}
foreach (var item in updates)
{
_data[item.Key] = item.Value;
}
}
private void PurgeMetadata()
{
if (_metadata != null && _variables.Count == 0)
{
String[] lines = _metadata.Split(new char[] { ’\n’, ’\r’ }, StringSplitOptions.RemoveEmptyEntries);
_collectionName = lines[0];
for (int i = 1; i < lines.Length; i++)
{
IVariable variable = (IVariable)Plugin.Load(lines[i].Trim());
_variables.Add(variable.Command(”name”), variable);
}
}
}
public String SaveData(String name, String value)
{
IVariable variable;
_variables.TryGetValue(name, out variable);
String result = variable.Validate(value);
String data;
_data.TryGetValue(name, out data);
if (data != null)
{
data = result;
}
else
{
_data.Add(name, result);
}
return result;
}
public string SqlTable()
{
StringBuilder builder = new StringBuilder();
builder.AppendLine(”DECLARE ” + _collectionName + ” TABLE”);
builder.AppendLine(”(”);
int index = 0;
foreach (IVariable column in _variables.Values)
{
if (index < _variables.Values.Count – 1)
{
builder.AppendLine(column.Command(”sqldeclare”) + ”,”);
}
else
{
builder.AppendLine(column.Command(”sqldeclare”));
}
index++;
}
builder.AppendLine(”)”);
return builder.ToString();
}
}
Seuraavaksi esittelen koodin, johon tällä kaikella pyrittiin. Se on geneerinen ”sanoma”-luokka.
Tässä tapauksessa metadata on clientissa ja se lähtee ”sanoman” mukana palvelimelle. Jos palvelin on geneerinen, niin sen ei tarvitse etukäteen tietää, millaista dataa on tulossa.
Talletus voidaan tehdä esimerkiksi Azure Table Storageen, DocumentDB:n tai Sql Serverin kantaan. Tämä esimerkki tukee Sql-toteutusta, mutta se voisi tukea kaikkia edellisiä. Metadataan lisättäisiin uusi talletustapatieto.
String metaData = @”
Customer
Name,1,80,0,Bloki.Characters
City,1,11,l,Bloki.Characters
Account,0,50,u,Bloki.Characters”;
// Client code
DataCollection clientData = new DataCollection(metaData);
clientData.SaveData(”Name”, ”Jarmo”);
clientData.SaveData(”City”, ”Helsinki”);
clientData.SaveData(”Account”, ”abcdefg”);
String jsonToSend = JsonConvert.SerializeObject(clientData);
// Server code
DataCollection serverData = JsonConvert.DeserializeObject(jsonToSend);
serverData.Validate();
String table = clientData.SqlTable();
Yllä clientilla ensin luodaan DataCollection metaData merkkijonon avulla. Sitten asetetaan ja tarkistetaan arvot. Seuraavaksi serialisoidaan data merkkijonoksi, joka lähetetään serverille (tietenkin geneerisellä kutsulla). Alla jsonToSend sisältö.
{”n”:”Customer”,”m”:”\r\nCustomer\r\nName,1,80,0,Bloki.Characters\r\nCity,1,11,l,Bloki.Characters\r\nAccount,0,50,u,Bloki.Characters”,”d”:{”Name”:”Jarmo”,”City”:”helsinki”,”Account”:”ABCDEFG”}}
Serverillä sitten deserialisoidaan saatu merkkijono ja suoritetaan tarkastus. Samaa koodia siis ajetaan molemmissa päissä.
Clienttiin ei luoteta, siksi tarkastus suoritetaan myös serverillä. Samalla estetään Sql injection -tyyppiset hyökkäykset.
Eräänä esimerkkinä geneerisen mallin hyödyllisyydesta voi mainita, että Sql-taulun rakenne voidaan pyytää luokalta. Varsinkin sovelluksen kehityksen aikana on kätevää luoda ja korjata taulut automaattisesti. Tällöin luokkaan tehdään Sql insert -lauseen luonti. Jos tulee exception, niin tutkintaan, oliko taulu olemassa. Jos ei ollut, niin luodaan se ja sitten yritetään talletusta uudestaan. Samoin jos on tullut uusi kenttä, luodaan se jne. Alla table-muuttujan sisältö.
DECLARE Customer TABLE
(
Name NVARCHAR (80),
City NVARCHAR (11),
Account NVARCHAR (50)
)
Kun geneerisiä toteutuksia tekee useaan eri tarkoitukseen, niin uuden sovelluksen saa tehtyä aika vaivattomasti.