Return array in CLR function - c#

I need to download regularly the data from link like below and save it to the MS database. I made CLR function with WebClient class but everythig is in one row, I need to separate it.
I got the idea to save the data in the array, use split and return it in loop but I don't know how to return line by line to save it in database.
public partial class UserDefinedFunctions
{
private static readonly WebClient webClient = new WebClient();
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString DownloadSynop(string uri)
{
string synop = webClient.DownloadString(uri);
string[] lines = synop.Split(new string[] { Environment.NewLine, "\n", "\"r" }, StringSplitOptions.None);
for (int i=0; i<lines.Length - 1; i++)
{
string kod = lines[i];
}
return new SqlString(kod); //problem
}
}

SQL Server does not really support “Arrays” per say and in general I would recommend that you develop a separate service or application that parses the web page and then simply insert the needed data into a table formatted for your needs. Using a CLR to query a web page means that you would have to publish the CLR as Unsafe to SQL Server. Some organization will not allow CLR’s marked as unsafe on their servers.
Having said that you can create a table valued CLR function. This will allow you to query your result from your function like a standard table. Below is a code example of how you can achieve this:
public partial class UserDefinedFunctions
{
private struct Record
{
public int RowNr;
public string SampleValue;
}
[SqlFunction(FillRowMethodName = "MyClrTableValuedFunction_FillRow",
TableDefinition = "[RowNr] INT, [SampleValue] NVARCHAR(50)")]
public static IEnumerable MyClrTableValuedFunction()
{
ArrayList list = new ArrayList();
for (int sampleRowNr = 0; sampleRowNr < 100; sampleRowNr++)
{
Record sampleRecord = new Record();
sampleRecord.RowNr = sampleRowNr;
sampleRecord.SampleValue = string.Format("Sample Value: {0}", sampleRowNr);
list.Add(sampleRecord);
}
return list;
}
public static void MyClrTableValuedFunction_FillRow(Object obj, out SqlInt32 rowNr, out SqlString sampleValue)
{
Record record = (Record)obj;
rowNr = record.RowNr;
sampleValue = record.SampleValue;
}
}
You can call your function as a standard select statement in SQL Server as follow:
SELECT [RowNr]
,[SampleValue]
FROM [dbo].[MyClrTableValuedFunction]()

Related

Output elements from a struct array

I'm tasked to ask the user for their data (name, last name, email & password), but I'm constrained to only using 25 lines per method which means that I have to use MANY of them. I'm using a struct for the data, and vectors for each user. However, when I want to display the users information it shows completely blank or just the direction of the array. I've tried many things with little to no success.
Here's the code in question.
using System;
public struct Usuario
{
public String name;
public String lastname;
public String email;
public String phone;
public String password;
}
static void Main()
{
Console.Clear();
Usuario[] user = new Usuario[5];
Array.Copy(user, UserNew(), 5);
UserView(user);
}
static Usuario[] UserNew()
{
int control = 1, i = 0;
Console.Clear();
Usuario[] register = new Usuario[5]; //It's supposed to have 5 users
Console.Write("\t\t¡Bienvenido al registro de usuario!\n\n");
while(control == 1)
{
register[i].name = Name();
register[i].lastname = LastName();
register[i].email = Email();
register[i].phone = Phone();
register[i].password = Psswrd();
Console.Write("\n\n\t¿Desea registrar otro usario?\n\tDigite 1 para registrar otro: ");
control = int.Parse(Console.ReadLine());
i++;
}
Menu();
return register;
}
static void UserView(Usuario[] UserReg)
{
UserReg = new Usuario();
Console.Write("\n\tIntroduzca el código de acceso: ");
String access = Asterisk('*');
if (access == "123456")
{
for (int i = 0; i <= 2; i++)
{
Console.Write("\n" + UserReg[i]);
}
}
else
{
Console.Write("\n\n\tEl código de acceso es incorrecto!");
Thread.Sleep(2000);
Menu();
}
}
This is a perfect example of why you should ALWAYS read the relevant documentation, especially when something doesn't work as you expect. If you had debugged your code then you would have seen that UserNew returns an array as expected but, after calling Array.Copy, it's contents is not copied to the user array. If you had then consulted the documentation for the Array.Copy method then you would have seen that the first parameter is the source and the second is the destination. You're copying the empty array over the full one, instead of the other way around. If you pass the source and destination arrays in the correct order, things will work as expected:
Array.Copy(UserNew(), user, 5);
That said, the way you're doing things doesn't really make much sense. You start by creating an array with a number of empty items, then you create another array with populated items, then you copy the data from the populated array to the empty one. Why not just use the populated array? This:
Usuario[] user = new Usuario[5];
Array.Copy(user, UserNew(), 5);
would more sensibly be written like this:
Usuario[] user = UserNew();
Now you can't mess up the copy because you're not doing it. You also don't use double the data for no reason.
EDIT:
The code seems worse than I first thought, so I'm going to provide a basic outline of how it ought to work and you can fill in the blanks. You should have method to get the data, e.g.
static User[] GetUsers()
{
var users = new User[5];
for (var i = 0; i < users.Length; i++)
{
// Gather input and set users[i]
}
return users;
}
and a method to display data, e.g.
static void DisplayUsers(User[] users)
{
foreach (var user in users)
{
// Display user
}
}
and then you should call one to get the data and then pass that data into the other, e.g.
var users = GetUsers();
DisplayUsers(users);
You almost certainly wouldn't use an array like that in a real app, because you generally wouldn't know how many users you were going to have. If your assignment requires you to use arrays though, this is pretty much how you should do it.

Why Can't I Populate an Array inside a method declaration in C#?

I am writing an application where I need to read a set of JSON files and create objects from a model in my application. This doesn't seem all that difficult and the code looks right to me, but I am using an array to store the JSON strings, and for some reason Visual Studio is red underlining the array name and saying it's an "unassigned local variable", even though I declare it before the foreach loop.
I'm somewhat of a novice in C#, so if someone could let me know how to correct this I would greatly appreciate it.
The line in question starts with "lotRanges[i] = JsonConvert..."
namespace InternalReceiptImport.Services
{
interface ILotRangeService
{
List<LotRange> GetAll();
}
public class LotRangeService : ILotRangeService
{
public List<LotRange> GetAll()
{
string jsonFilePath = #"\Data";
Array files = Directory.GetFiles(jsonFilePath);
LotRange[] lotRanges;
int i = 0;
foreach (string filename in files)
{
string filepath = jsonFilePath + "\\" + filename;
string json = File.ReadAllText(filepath);
lotRanges[i] = JsonConvert.DeserializeObject<LotRange>(json);
i++;
}
List<LotRange> listLotRanges = lotRanges.ToList();
return listLotRanges;
}
}
}
It was suggested below I just use a list instead of an array. I tried that, but it's giving me the same error on the line I am using to add to the list. Here is the code...
namespace InternalReceiptImport.Services
{
interface ILotRangeService
{
List<LotRange> GetAll();
}
public class LotRangeService : ILotRangeService
{
public List<LotRange> GetAll()
{
string jsonFilePath = #"\Data";
Array files = Directory.GetFiles(jsonFilePath);
List<LotRange> listLotRanges;
int i = 0;
foreach (string filename in files)
{
string filepath = jsonFilePath + "\\" + filename;
string json = File.ReadAllText(filepath);
listLotRanges.Add(JsonConvert.DeserializeObject<LotRange>(json));
i++;
}
return listLotRanges;
}
}
}
in both your examples the problem is that lotRanges is declared but it has not been assigned a value yet, that is, it is null. In order to fix this, all you have to do is assign a value to your declared variable. In the array case, you have to define the size of it upfront:
Array files = Directory.GetFiles(jsonFilePath);
LotRange[] lotRanges = new LotRange[files.Length];
And in the case of using a List<LotRange> you don't need to know the size upfront, which is one of the reasons why people tend to prefer using List<T> for scenarios like this.
List<LotRange> lotRanges = new List<LotRange>();

Take too long time to write in to Access Data Base using C# WPF application, Visual Studio,

I have a WPF application using C# and VS.
And I am using an Access database.
I have a loop that has to run in a maximum time of 500MS, But its take 570+-
In my program, I have a wait time of ~340MS in total and more ~160MS that I can to optimize
After checking with a Stopwatch I found that when I write my data to my Access Database Its take about ~50MS (I have a 3 writes to there).
And I have no Idea how to optimize my Database write
My Class that connect and using the database is an external DLL file
that look like that (I also give an example of one method that take a 50MS of runtime, named as "AddDataToLocalHeaderResult"):
namespace DataBaseManager
{
public class LocalPulserDBManager
{
private string localConnectionString;
private string databaseName = $#"C:\Pulser\LocalPulserDB.mdb";
private readonly int _30DaysBack = -30;
private static readonly Lazy<LocalPulserDBManager> lazy =new Lazy<LocalPulserDBManager>(() => new LocalPulserDBManager());
public static LocalPulserDBManager LocalPulserDBManagerInstance { get { return lazy.Value; } }
private void CreateConnectionString()
{
localConnectionString = $#"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={databaseName};Persist Security Info=True";
}
private LocalPulserDBManager()
{
CreateConnectionString();
}
public void AddDataToLocalHeaderResult(string reportNumber,string reportDescription,
string catalog,string workerName,int machineNumber, Calibration c,string age)
{
if (IsHeaderLocalDataExist(reportNumber, catalog, machineNumber, c) == false)
{
using (OleDbConnection openCon = new OleDbConnection(localConnectionString))
{
string query = "INSERT into [HeaderResult] ([ReportNumber],[ReportDescription],[CatalogNumber], " +
"[WorkerName], [LastCalibrationDate], [NextCalibrationDate], [MachineNumber], [Age]) " +
"VALUES (#report ,#reportDescription ,#catalog, #workerName," +
" #LastCalibrationDate, #NextCalibrationDate, #machineNumber, #age)";
using (OleDbCommand command = new OleDbCommand(query))
{
command.Parameters.AddWithValue("#report", reportNumber);
command.Parameters.AddWithValue("#reportDescription", reportDescription);
command.Parameters.AddWithValue("#catalog", catalog);
command.Parameters.AddWithValue("#workerName", workerName);
command.Parameters.AddWithValue("#LastCalibrationDate", c.LastCalibrationDate);
command.Parameters.AddWithValue("#NextCalibrationDate", c.NextCalibrationDate);
command.Parameters.AddWithValue("#machineNumber", machineNumber);
command.Parameters.AddWithValue("#age", age);
command.Connection = openCon;
openCon.Open();
int recordsAffected = command.ExecuteNonQuery();
openCon.Close();
}
}
}
}
....
....
METHODS
....
}
}
In my executable program I use that like that :
I have usings as that : using static DataBaseManager.LocalPulserDBManager;
and in my code I exeute the method like that LocalPulserDBManagerInstance.AddDataToLocalHeaderResult(ReportNumber, Date_Description,CatalogNumber, WorkerName, (int)MachineNumber, calibrationForSave, AgeCells);
One of my access database table look like that :
One row in that table look like that:
50MS it is normal runtime in that situation?
If here is missing any information please tell me...
********************* EDITING **************************
I have change my AddDataToLocalHeaderResult method as the first command told me
I got the same result
public void AddDataToLocalHeaderResult(string reportNumber,string reportDescription,
string catalog,string workerName,int machineNumber, Calibration c,string age)
{
if (IsHeaderLocalDataExist(reportNumber, catalog, machineNumber, c) == false)
{
using (OleDbConnection openCon = new OleDbConnection(localConnectionString))
{
string query = "INSERT into [HeaderResult] ([ReportNumber],[ReportDescription],[CatalogNumber], " +
"[WorkerName], [LastCalibrationDate], [NextCalibrationDate], [MachineNumber], [EditTime], [Age]) " +
"VALUES (#report ,#reportDescription ,#catalog, #workerName," +
" #LastCalibrationDate, #NextCalibrationDate, #machineNumber,#edittime, #age)";
DateTime dt = DateTime.Now;
DateTime edittime = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second);
using (OleDbCommand command = new OleDbCommand(query))
{
command.Parameters.AddWithValue("#report", reportNumber);
command.Parameters.AddWithValue("#reportDescription", reportDescription);
command.Parameters.AddWithValue("#catalog", catalog);
command.Parameters.AddWithValue("#workerName", workerName);
command.Parameters.AddWithValue("#LastCalibrationDate", c.LastCalibrationDate);
command.Parameters.AddWithValue("#NextCalibrationDate", c.NextCalibrationDate);
command.Parameters.AddWithValue("#machineNumber", machineNumber);
command.Parameters.AddWithValue("#edittime", edittime);
command.Parameters.AddWithValue("#age", age);
command.Connection = openCon;
openCon.Open();
int recordsAffected = command.ExecuteNonQuery();
openCon.Close();
}
}
}
}
Using the method you're showing here, you're adding one row at a time. So the server is opening a connection to the db, the data's being written to memory, then to the physical file (mdb), then the indexes are being updated. To that's a full four steps per row you're trying to execute. Worse than that, the data write to the physical file is time consuming.
I think that if you use a different approach, do these four steps (connection, memory, data write, re-index) for the entire set of data you're trying to insert. So, let's say you're adding 1000 records, rather than 4000 steps (4x1000), you could reduce this processing to 1400 processing steps (1 connection, super-fast 1000 memory writes, 1 data file write, 1 index revision).
The following code gives the rough idea of what I'm talking about:
class Program
{
static void Main(string[] args)
{
//memory-only list for data loading
List<HeaderResult> mylist = new List<HeaderResult>(){ new HeaderResult("report1","desc of report","ete"), new HeaderResult("report2", "desc of report2", "ete2")};
var tableForInsert = new DataTable();
using (SqlDataAdapter dataAdapter = new SqlDataAdapter("SELECT * from HeaderResult", "my conneciton string")) {
dataAdapter.Fill(tableForInsert);
//now I have a live copy of the table into which I want to insert data
//blast in the data
foreach (HeaderResult hr in mylist) {
tableForInsert.Rows.Add(hr);
}
//now all the data is written at once and sql will take care of the indexes after the datat's written
dataAdapter.Update(tableForInsert);
}
}
//class should have same fields as your table
class HeaderResult
{
string report;
string reportDescription;
string etc;
public HeaderResult(string rpt, string desc, string e)
{
report = rpt;
reportDescription = desc;
etc = e;
}
}

SSIS 2008 Script in C#: need to convert Y2K string to date format and use it to populate variable

I am trying to get records from an AS400 system into Dynamics CRM programatically. To achieve this i have pushed the AS400 records into a SQL table and am able to push those records to CRM by referencing the CRM 4 web service endpoints in a SSIS 2008 C# script.
The problem is one of the fields is in Y2K date string format. In order to get it into a date field (D.O.B) in CRM i believe i will need to convert it to a date format then reference resulting value in a variable.
I do not know how to do this.
This question/answer (http://stackoverflow.com/a/4880021/1326443) may help with part of the question but i do not know how to use this into my script to get a value (haven't done any scripting for a number of years and new to C#)
Script snippet:
public class ScriptMain : UserComponent
{
private CrmService service = null;
public override void PreExecute()
{
base.PreExecute();
CrmAuthenticationToken token = new CrmAuthenticationToken();
token.AuthenticationType = 0;
token.OrganizationName = "DevOrg";
service = new CrmService();
service.Url = "http://crm/mscrmservices/2007/crmservice.asmx";
service.CrmAuthenticationTokenValue = token;
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
}
public override void PostExecute()
{
base.PostExecute();
}
public override void LeadInput_ProcessInputRow(LeadInputBuffer Row)
{
lead cont = new lead();
if (!Row.TITL20_IsNull)
{
cont.salutation = Row.TITL20;
}
if (!Row.DOBI20_IsNull)
{
cont.new_birthdate = Row.DOBI20;
}
....
....
service.Create(cont);
}
}
}
{ cont.new_birthdate = Row.DOBI20; } throws:
cannot implicitly convert type 'string' to .....CrmSdk.CRMDateTime
Just had a look at the documentation for CRMDateTime (http://msdn.microsoft.com/en-us/library/bb928935.aspx)
This states that you can set this using the Value property (http://msdn.microsoft.com/en-us/library/bb928944.aspx)
So you might like to try:
cont.new_birthdate.Value = Row.DOBI20
Edit
In response to your comments, try the following
string ConvertDate(string dateToConvert)
{
dateToConvert= dateToConvert.PadLeft(7, '0');
int c;
int.TryParse(dateToConvert.Substring(0,1), out c);
c = (c * 100) + 1900;
int y;
int.TryParse(dateToConvert.Substring(1,2), out y);
return (c+y).ToString() + dateToConvert.Substring(3,4);
}

How to prevent untrusted string parameter in C#

For security reasons, I don't want specific method to receive non-programmer or non-compiler time strings, how could I do this?
readonly String OK_STR = "some text";
String BAD_STR = "another text";
public void SetSecureStr(String str)
{
//Use the string for security purpose
}
//Somewhere in the code
SetSecureStr(OK_STR); //Accepted
SetSecureStr(OK_STR + "Programmer passed this staticlly!"); //Accepted (If not possible to implement, forget about it)
SetSecureStr(BAD_STR); //Throw exception, BAD_STR is modifiable
SetSecureStr(OK_STR + untrustedVar); //Throw exception, concatenation with modifiable
SetSecureStr(String.Format("{0}", OK_STR)); //Throw exception, not const
It may be better to whitelist against things inside your ability to control, such as enums or local constants (or a local whitelist from configuration data if the list isn't fixed ahead of time).
As a rough check, you could check whether it is interned, since all literals will be interned automatically via ldstr; but note you can explicitly intern too, so this isn't 100% safe.
And of course, in any event with the question as asked, if that string happens somewhere else as a literal (unconnected to this code) it would still be trusted. I suggest a whitelist is safer...
A whitelist could be as simple as:
private static readonly HashSet<string> whiteList = new HashSet<string> {
"good", "more good"
};
... check via whiteList.Contains(s)
but note that this is still mutable at runtime (via reflection if necessary).
Instead of accepting string values, number all your strings, and pass the number:
string[] good_strings = {"some text"};
public void SetSecureStr(int stringno)
{
string s = good_strings[stringno];
}
Computed strings won't be supported with that approach.
I came finally with a solution which is hybrid between the two previously proposed answers:
public class SqlQuery
{
private SqlQuery() { }
private static UInt32 sqlQueriesCount = 0;
public static UInt32 INBOUND_UPDATE_CALLBACK_NUM = sqlQueriesCount++;
public static UInt32 INBOUND_UPDATE_DEST_ADDR_SUBUNIT = sqlQueriesCount++;
public static UInt32 INBOUND_UPDATE_DEST_BEARER_TYPE = sqlQueriesCount++;
//...etc
private static readonly Dictionary<UInt32, String> queries = new Dictionary<UInt32, String>
{
{SqlQuery.INBOUND_UPDATE_CALLBACK_NUM, "UPDATE inbound SET callbackNum = ? WHERE id = ?"},
{SqlQuery.INBOUND_UPDATE_DEST_ADDR_SUBUNIT, "UPDATE inbound SET destAddrSubunit = ? WHERE id = ?"},
{SqlQuery.INBOUND_UPDATE_DEST_BEARER_TYPE, "UPDATE inbound SET destBearerType = ? WHERE id = ?"},
//...etc
};
public static String GetQueryText(UInt32 queryKey)
{
String query = null;
if (SqlQuery.queries.TryGetValue(queryKey, out query) == false)
{
throw new ArgumentOutOfRangeException(String.Format("Query must be paramerized query stored within SqlQuery class, provided queryKey: {0}", queryKey));
}
return query;
}
}
Usage:
OdbcCommand cmd = new OdbcCommand(SqlQuery.GetQueryText(SqlQuery.INBOUND_UPDATE_CALLBACK_NUM), con);

Categories

Resources