I have a web service running on a local server. In it I have a method that returns a json representation of an Oracle database query, which is converted from a List<Dictionary<String, String>> object.
I have a GUI program that needs the same data. If I try to move the query to the GUI, I have to require the user to install Oracle on there system, according to what I read after getting the following error:
Could not load file or assembly 'Oracle.DataAccess, Version=...'
I don't want to make my users install Oracle on their system.
My solution has been to just access the existing logic running on a web service (.NET 3.5 WebForms App). I wrote another web service that returns the List<Dictionary<String, String>> object, instead of going through the hassle of converting it to a json object.
How can I get this from the GUI?
In my initial searches I found a few links that said it was possible, or some where they returned an object in json format. But nothing for what I was looking for explicitly.
Any help?
not quite following you but how about a web api? here is one I was playing around with using the hr schema. so create a web api project. then open the tools library package manager and add the oracle driver. type in Install-Package odp.net.managed then configure your connection string in your web config here is mine just a database on my local machine so I'll leave the password in.
<add name="hr" connectionString="Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SID=mydatabase))); User Id=hr; Password=fdsafdsafafads;" providerName="oracle.manaagedatacess.client" />
then create your model/s here is one employees
public class employee{
public int employee_id { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public string email { get; set; }
public string phone_number { get; set; }
public DateTime hire_date { get; set; }
public string job_id { get; set; }
public decimal? salary { get; set; }
public decimal? commission_pct { get; set; }
public int? manager_id { get; set; }
public int? department_id { get; set; }
}
I then made a repository (if you use entity framework as opposed to ado you don't need these steps but I don't typically use entity framework)
namespace webApiOracle.Models
{
internal class employeeRepository
{
internal static IEnumerable<employee> getAllEmployees()
{
List<employee> employees = new List<employee>();
string sql = "SELECT * from employees";
OracleDataReader rdr = dataHelper.getrdr(sql);
if (rdr.HasRows)
{
while (rdr.Read())
{
employee emp = getEmployee(rdr);
employees.Add(emp);
}
rdr.Close();
}
return employees;
}
internal static employee getEmployee(int id)
{
employee emp = null;
string sql = "SELECT * from employees where employee_id = " + id;
OracleDataReader rdr = dataHelper.getrdr(sql);
if (rdr.HasRows)
{
while (rdr.Read())
{
emp = getEmployee(rdr);
}
rdr.Close();
}
return emp;
}
internal static employee add(employee emp)
{
OracleDataAdapter oda = new OracleDataAdapter();
string sql = "Insert into employees ";
sql = sql + "(EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,SALARY,COMMISSION_PCT,MANAGER_ID,DEPARTMENT_ID) ";
sql = sql + "values (EMPLOYEES_SEQ.NEXTVAL,:first_name,:last_name,:email,:phone_number,sysdate,:job_id,:salary,:commission_pct,:manager_id,:department_id)";
OracleConnection cn =
new OracleConnection(
ConfigurationManager.ConnectionStrings
["hr"].ConnectionString);
cn.Open();
oda.InsertCommand = new OracleCommand(sql, cn);
oda.InsertCommand.BindByName = true;
oda.InsertCommand.Parameters.Add(":first_name", emp.first_name);
oda.InsertCommand.Parameters.Add(":last_name", emp.last_name);
oda.InsertCommand.Parameters.Add(":email", emp.email);
oda.InsertCommand.Parameters.Add(":phone_number", emp.phone_number);
oda.InsertCommand.Parameters.Add(":job_id", emp.job_id);
oda.InsertCommand.Parameters.Add(":salary", emp.salary);
oda.InsertCommand.Parameters.Add(":commission_pct", emp.commission_pct);
oda.InsertCommand.Parameters.Add(":manager_id", emp.manager_id);
oda.InsertCommand.Parameters.Add(":department_id", emp.department_id);
int count = oda.InsertCommand.ExecuteNonQuery();
sql = "SELECT * from employees where employee_id = EMPLOYEES_SEQ.CURRVAL";
OracleDataReader rdr = dataHelper.getrdr(sql);
if (rdr.HasRows)
{
while (rdr.Read())
{
emp = getEmployee(rdr);
}
rdr.Close();
}
return emp;
}
internal static IEnumerable<employee> getEmployeesByVal(string key, string value)
{
List<employee> employees = new List<employee>();
string sql = "SELECT * from employees where " + key + " = '" + value + "'";
OracleDataReader rdr = dataHelper.getrdr(sql);
if (rdr.HasRows)
{
while (rdr.Read())
{
employee emp = getEmployee(rdr);
employees.Add(emp);
}
rdr.Close();
}
return employees;
}
private static employee getEmployee(OracleDataReader rdr)
{
employee emp = new employee
{
employee_id = rdr.GetInt32(rdr.GetOrdinal("EMPLOYEE_ID")),
first_name = rdr["FIRST_NAME"].ToString(),
last_name = rdr["LAST_NAME"].ToString(),
email = rdr["EMAIL"].ToString(),
phone_number = rdr["PHONE_NUMBER"].ToString(),
hire_date = rdr.GetDateTime(rdr.GetOrdinal("HIRE_DATE")),
job_id = rdr["JOB_ID"].ToString(),
salary = dataHelper.decimalnullable(rdr, "SALARY"),
commission_pct = dataHelper.decimalnullable(rdr, "COMMISSION_PCT"),
manager_id = dataHelper.intnullable(rdr, "MANAGER_ID"),
department_id = dataHelper.intnullable(rdr, "DEPARTMENT_ID")
};
return emp;
}
}
}
then make a web api controller go to the controller folder right click and pick empty web api controller.
public class employeeController : ApiController
{
public IEnumerable<employee> GetAllEmployees()
{
return employeeRepository.getAllEmployees();
}
//[Route("api/employee/{id:int}")]
public employee getEmployee(int id) {
return employeeRepository.getEmployee(id);
}
[Route("api/employee/{key}/{value}")]
public IEnumerable<employee> getEmployeesByVal(string key, string value) {
return employeeRepository.getEmployeesByVal(key, value);
}
[HttpPost]
public employee add(employee emp)
{
return employeeRepository.add(emp);
}
}
}
then when you want your json data just call the url so if I want all employees my url is /api/employee if you want just one employee it with id of 100 would be /api/employee/100 if you want employees with last name king it would be /api/employee/last_name/king
there are tons of web api examples out there and a couple free ebooks if you want to check it out.
Related
In my ASP.Net Core-6 Web API, I am implementing SqlClient in ADO.NET Core. I want to select employees by employment date.
I have this entity (table):
public class Employee
{
public int EmployeeId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Email { get; set; }
public string EmploymentDate { get; set; }
}
Then I created this stored procedure in the SQL Server DB:
CREATE PROCEDURE [dbo].[sp_employees]
#pdStartDate datetime,
#pdEndDate datetime
AS
SELECT
*
FROM
[Employees].[dbo].[employees]
WHERE
EmployementDate BETWEEN #pdStartDate AND #pdEndDate
RETURN 1
I want to spool employees between a range of selected employment date using ADO.NET Core SqlClient. I have written this code:
public IEnumerable<Employee> GetEmployees()
{
List<Employee> employeelist = new List<Employee>();
using (con = new SqlConnection(connection))
{
con.Open();
command = new SqlCommand("sp_employees", con);
command.CommandType = CommandType.StoredProcedure;
dataReader = command.ExecuteReader();
while (dataReader.Read())
{
Employee employee = new Employee();
employee.EmployeeId = Convert.ToInt32(dataReader["EmployeeId"]);
employee.Firstname = dataReader["Firstname"].ToString();
employee.Lastname = dataReader["Lastname"].ToString();
employee.Email = dataReader["Email"].ToString();
employee.EmploymentDate = Convert.ToDateTime(dataReader["EmploymentDate"].ToString());
employeelist.Add(employee);
}
con.Close();
}
return employeelist;
}
How do I modify the code above to include the StartDate and EndDate of the EmploymentDate in the stored procedure?
You could use:
con.Open();
command = new SqlCommand("sp_employees", con);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#pdStartDate", startDate);
command.Parameters.AddWithValue("#pdEndDate", endDate);
dataReader = command.ExecuteReader();
When getting data from a database and creating a list of objects from each row which comes to around 2000 objects in the list. When trying to return this list it takes enough time to time out well over 2 minutes. How can I cut this time down? filling the list takes no time at all it is only when returning it from the api call
Below is my call
[HttpGet] //API GET Call
[Route("List", Name = "Contractor List")] //API Route
//Description
[ProducesResponseType(typeof(IEnumerable<ContractorSummary>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(string), StatusCodes.Status429TooManyRequests)]
public IActionResult GetContractorList(string status = "A", string locationId = "00")
{
//Check if API Key is valid
if (Utilities.Helpers.IsValidApiKey(SubscriptionKey) == false)
return Unauthorized(); //Invalid Key
//Create an array of parameters
var parameters = new SqlParameter[]
{
new SqlParameter ("#subkey", SubscriptionKey),
new SqlParameter ("#status", status),
new SqlParameter ("#locationid", locationId)
};
//Create results DataTable and set it to GetDataTableSP sending SP name and parameters
var results = Utilities.Data.GetDataTableSP("sp_API_GetContractor", parameters);
var contractors = new List<ContractorSummary>();
foreach (DataRow row in results.Rows)
{
contractors.Add(new ContractorSummary
{
Client = row["client"].ToString(),
ICID = row["icid"].ToString(),
ID2 = row["id2"].ToString(),
ICNUM = Convert.ToInt32(row["ICNUM"].ToString()),
Location = row["location"].ToString(),
LastName = row["LastName"].ToString(),
FirstName = row["firstname"].ToString(),
Company = row["company"].ToString(),
EIN = row["ein"].ToString(),
State = row["state"].ToString(),
LastPaid = DateTime.Parse(row["lastpaid"].ToString()),
Status = row["status"].ToString()
//?
//Self = "/contractor/detail/?icnum=" + row["ICNUM"],
//Profile= "/contractor/profile/?icnum=" + row["ICNUM"],
//Documents = "/contractor/documents/?icnum=" + row["ICNUM"]
});
}
//Log Action
Helpers.LogAction(SubscriptionKey, ControllerContext.ActionDescriptor.ActionName, DateTime.Now, Request.HttpContext.Connection.RemoteIpAddress.ToString());
return Ok(contractors);
}
This is the object
public class ContractorSummary
{
public string Status { get; set; }
public string LocationId { get; set; }
public string Client { get; set; }
public string ICID { get; set; }
public string ID2 { get; set; }
public int ICNUM { get; set; }
public string Location { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Company { get; set; }
public string EIN { get; set; }
public string State { get; set; }
public DateTime LastPaid { get; set; }
//public string Self { get; set; }
//public string Profile { get; set; }
//public string Documents { get; set; }
}
This is how I am getting the datatable
public static DataTable GetDataTableSP(string spName, SqlParameter[] parameters)
{
//Variables
var dsDataTable = new DataTable();
var da = new SqlDataAdapter();
var conn = new SqlConnection(BuildConnectionString());
try
{
// open connection
conn.Open();
//var command = new SqlCommand(sql, conn);
// Create our Sql Command & give it properties
var command = new SqlCommand(spName, conn);
command.CommandType = CommandType.StoredProcedure;
foreach (SqlParameter p in parameters)
{
command.Parameters.Add(p);
}
da.SelectCommand = command;
da.Fill(dsDataTable);
da.Dispose();
// close our connection
conn.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (conn != null)
conn.Dispose(); // ensure connection is closed in the event of a crash
}
return dsDataTable;
}
It turns out when testing through visual studio it was using chrome which was causing the hang up. When switching to postman it worked flawlessly.
I'm new to web service. I'm experimenting on how to retrieve more than 1 value from a sql query in a web service. I tried with some tutorial and below is my attempt. I know I'm nearly there but when I debug the following code. There is no error in the code but the method did not appear on the web service at all. Please help and tell me what is exactly wrong or should there be another approach of doing this?
public class WebService1 : System.Web.Services.WebService
{
public class Record
{
public int userid { get; set; }
public string username { get; set; }
public string city { get; set; }
public int age { get; set; }
}
[WebMethod]
public static List<Record> GetData(int age)
{
age = 1;
SqlConnection con = new SqlConnection("Data Source=DIT-NB1260382;Initial Catalog=Experiment;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand("SELECT userid, username, city, age FROM userdet where age = '" + age + "'", con);
SqlDataReader dr = cmd.ExecuteReader();
List<Record> records = new List<Record>();
while (dr.Read())
{
records.Add(new Record()
{
userid = dr.GetInt32(0),
username = dr.GetString(1),
city = dr.GetString(2),
age = dr.GetInt32(3),
});
}
dr.Close();
con.Close();
return records;
}
}
}
I have data as listed below in database.
I have following data access layer code that is working for simple scenarios.. But for the above scenario, I need result based on employeeID grouping.. All roles for an employee should come under one Employee object.
How can we achieve this by modifying the following data access code using the generic delegate features of C# ?
Note: I am looking for a solution that does not use DataTable (since DataTable loads all data upfront and is slower than the IDataRecord approach).
REFERENCES
An Elegant C# Data Access Layer using the Template Pattern and Generics
Using C# generics and factory classes to map IDataReader to POCO
Data Transfer Object
public class Role
{
public int RoleID { get; set; }
public string RoleName { get; set; }
}
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public List<Role> Roles { get; set; }
//IDataRecord Provides access to the column values within each row for a DataReader
//IDataRecord is implemented by .NET Framework data providers that access relational databases.
//Factory Method
public static Employee EmployeeFactory(IDataRecord record)
{
return new Employee
{
EmployeeID = (int)record[0],
EmployeeName = (string)record[1]
};
}
}
Common DAL
public class MyCommonDAL
{
public static IEnumerable<T> ExecuteQueryGenericApproach<T>(string commandText, List<SqlParameter> commandParameters, Func<IDataRecord, T> factoryMethod)
{
string connectionString = #"Server=TRVMVSDDVXXXX;Database=AS400_Source;User Id=XXXXXXXX;Password=XXXXXXX";
//Action, Func and Predicate are pre-defined Generic delegates.
//So as delegate they can point to functions with specified signature.
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection;
command.CommandType = CommandType.Text;
command.CommandText = commandText;
command.CommandTimeout = 0;
command.Parameters.AddRange(commandParameters.ToArray());
connection.Open();
using (var rdr = command.ExecuteReader())
{
while (rdr.Read())
{
yield return factoryMethod(rdr);
}
rdr.Close();
}
}
}
}
}
Specific DAL
public class MyEmployeeDAL
{
public List<Employee> GetEmployees(string excludedEmployee)
{
List<SqlParameter> commandParameters = new List<SqlParameter>()
{
new SqlParameter {ParameterName = "#ExcludedEmployee",
Value = excludedEmployee,
SqlDbType = SqlDbType.VarChar}
};
string commandText = #"SELECT E.EmployeeID,E.EmployeeName,R.RoleID,R.RoleName FROM dbo.EmployeeRole ER
INNER JOIN dbo.Employee E ON E.EmployeeID= ER.EmployeeID
INNER JOIN dbo.[Role] R ON R.RoleID= Er.RoleID
WHERE EmployeeName <> #ExcludedEmployee";
IEnumerable<Employee> employees = MyCommonDAL.ExecuteQueryGenericApproach<Employee>(commandText, commandParameters, Employee.EmployeeFactory);
return employees.ToList();
}
}
Client
static void Main(string[] args)
{
MyEmployeeDAL logDAL = new MyEmployeeDAL();
List<Employee> logSeverities = logDAL.GetEmployees("test");
}
You should add new flat class
public class RoleAndEmployee
{
public int RoleID { get; set; }
public string RoleName { get; set; }
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public static Employee EmployeeFactory(IDataRecord record)
{
return new RoleAndEmployee
{
EmployeeID = (int)record[0],
EmployeeName = (string)record[1],
RoleID = (int)record[2],
RoleName = (string)record[3]
};
}
}
and call (i hope, i write it correct without IDE):
IEnumerable<Employee> employees = MyCommonDAL.ExecuteQueryGenericApproach<RoleAndEmployee>(commandText, commandParameters, RoleAndEmployee.EmployeeFactory)
.GroupBy(c=>new {c.EmployeeId, c.EmployeeName}, c=>new{c.RoleId, c.RoleName})
.Select(k=>new Employee{EmployeeId=k.Key.EmployeeId, EmployeeName= k.Key.EmployeeName, Roles = k.ToList()});
Update:
If you don't want introduce flat class, you can use next approach:
public static Employee EmployeeFactory(IDataRecord record)
{
var employee = new Employee
{
EmployeeID = (int)record[0],
EmployeeName = (string)record[1],
Roles = new List<Role>()
};
employee.Roles.Add(new Role{RoleID = (int)record[2], roleName=(string)record[3]});
return employee;
}
IEnumerable<Employee> employees = MyCommonDAL.ExecuteQueryGenericApproach
<Employee>(commandText, commandParameters, Employee.EmployeeFactory)
.GroupBy(
x => new { x.EmployeeID, x.EmployeeName},
(key, group) =>
new Employee
{
EmployeeId=key.EmployeeID,
EmployeeName=key.EmployeeName,
Roles = group.SelectMany(v => v.Roles).ToList()
}).ToList();
For so to happen you need to assign values to Roles property of your Employee objects.
In factoryMethod you need to find distinct employees create object of the same and assign corresponding roles that you get from your query.
This may help you query your table that you have got as a result.
After executing logDAL.GetEmployees("test") in your specific DAL, just group them.
IEnumerable<Employee> employees = MyCommonDAL.ExecuteQueryGenericApproach
<Employee>(commandText, commandParameters, Employee.EmployeeFactory);
employees = employees.GroupBy(
x => new
{
x.EmployeeID,
x.EmployeeName
},
(key, groupedEmployees) =>
new Employee
{
EmployeeId=key.EmployeeID,
EmployeeName=key.EmployeeName,
Roles = groupedEmployees.SelectMany(v => v.Roles)
});
return employees.ToList();
I have this line in my sql server 2008 r2 database, but I don't understand:
2012-12-06 11:00:36.703 and is of type DATETIME
When I display it in my view it is:
Mon, Jan 1, 0001
Here my code:
var allNews = ZincService.NewsService.GetNewsPostsForId(id);
List<Zinc.Web.Areas.News.ViewModels.Home.NewsPostsViewModel> newsItems = new List<NewsPostsViewModel>();
foreach (var newsItem in allNews)
{
NewsPostsViewModel newsItemViewModel = new NewsPostsViewModel();
newsItemViewModel.CommentDate = String.Format("{0:ddd, MMM d, yyyy}", newsItem.CommentDate);
}
public class NewsPostsViewModel
{
public Entities.News.News MainNews { get; set; }
public virtual string News { get; set; }
public virtual int NewsId { get; set; }
public virtual string CommentDate { get; set; }
}
public List<DataModels.News.NewsPostsDataModel> GetNewsPostsForId(int id)
{
using (SqlConnection conn = new SqlConnection(ZincModelContainer.CONNECTIONSTRING))
{
using (SqlCommand cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "[News].[GetNewsPostsForId]";
SqlParameter param = new SqlParameter("#Id", System.Data.SqlDbType.Int);
param.Value = id;
cmd.Parameters.Add(param);
List<DataModels.News.NewsPostsDataModel> news = new List<DataModels.News.NewsPostsDataModel>();
using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
adapter.Fill(dt);
foreach (DataRow row in dt.Rows)
{
string message = Convert.ToString(row["NewsDescription"]);
news.Add(new DataModels.News.NewsPostsDataModel { NewsPostId = id ,Message = message});
}
}
return news;
}
}
}
public class NewsPostsDataModel
{
public virtual int NewsPostId { get; set; }
public virtual string Message { get; set; }
public virtual DateTime CommentDate { get; set; }
}
Can some one help me, please?
This is the problem:
news.Add(new DataModels.News.NewsPostsDataModel { NewsPostId = id ,Message = message});
You're not populating CommentDate here, so it's got the default value of DateTime.MinValue.
Of course we now can't tell whether your stored procedure returns the value at all, but assuming it does, you should presumably be fetching it from the DataRow and putting it in your model...
Before you just fix the code, you should take a step back and think about your diagnostic process. Did you try debugging into this? Adding logging? You should have been able to see very quickly that you didn't have a CommentDate in the model, and then tried to work out why you didn't have a CommentDate, which should have led you back to the code creating the instance of the model. If you can improve your diagnostic processes, you can speed you your future development significantly by not having to ask as many questions.