I need to retrieve data from an external SQL Server database and view it (not store it) in my ASP.NET MVC application. The only way I can connect to the server is by using a server name, port number and access to a SQL Server stored procedure that the server owners provide.
Currently the only way I know how to do this is by:
a) Writing a .sql script to retrieve the data. Note I can't see any of the SQL Server tables, I just have the name of the stored procedure and the criteria. I save the result as a .txt file
EXEC dbo.listData #Criteria = '<Portal><Data Name="Data" Format="Narrow" Interval="5m">
<Where>
<Column Name="Point" Project="XXX" Value="XXXX" Operator="LIKE" />
<Column Name="Point" Project="YYY" Value="YYYY" Operator="LIKE" />
</Where>
</Data>
</Portal>'
, #StartDateTime = '12/28/2020',
#EndDateTime = '12/29/2020'
b) creating a model class
public class Alarm_DataModel
{
public string Project { get; set; }
public string Point { get; set; }
}
c) Creating a controller to put the data into the model to pass to the view
public ActionResult Index()
{
string[] texts = System.IO.File.ReadAllLines(Server.MapPath("~/App_Data/Test/test.txt"));
texts = texts.Skip(2).ToArray();
List<Alarm_DataModel> Alarm_Data = new List<Alarm_DataModel>();
foreach (string row in texts)
{
if (!string.IsNullOrEmpty(row))
{
int x = row.Length;
Alarm_Data.Add(new Alarm_DataModel
{
Project = row.Substring(0, 25),
Point = row.Substring(26, 60), 6
});
}
}
ViewBag.Data = texts;
return View(Alarm_Data);
}
My question may have been answered many times, but I have looked and can't find anything that I can interpret.
Is there a way that I can obtain the data using my controller without having to rely on the .sql script being ran and generating the .txt file?
With my limited access to the database, what is the best way to query using the provided stored procedure and populating my model to pass to the view?
With Dapper the code would look something like this:
using Dapper;
using System.Data.SqlClient;
using System.Linq;
private IEnumerable<Alarm_DataModel> GetAlarmList()
{
var sql = #"EXEC dbo.listData #Criteria = '<Portal><Data Name=""Data"" Format=""Narrow"" Interval=""5m"">
<Where>
<Column Name=""Point"" Project=""XXX"" Value=""XXXX"" Operator=""LIKE"" />
<Column Name=""Point"" Project=""YYY"" Value=""YYYY"" Operator=""LIKE"" />
</Where>
</Data>
</Portal>'";
using( var connection = new SqlConnection("(connecting string here)") )
{
var values = new { StartDateTime = "2017.1.1", EndDateTime = "2017.12.31" };
return connection.Query<Alarm_DataModel>(sql, values).ToList();
}
}
[HttpGet]
public ActionResult Index()
{
var alarmList = GetAlarmList();
ViewBag.Data = "texts";
return View(alarmList);
}
If the Stored Procedure can execute and reply with some data, everything is working with the credentials you are given. This is a normal production security setup.
That you also want to view tables, is a different concern and can be solved with different credentials or access to a another server.
Thank you Frank Nielsen, your recommended code worked with just some minor edits with the criteria values. For some reason I needed to include them in the query for it to work.
Much appreciated, thanks
Here is the final code:
public class AlarmDataController : Controller
{
// GET: AlarmData
private IEnumerable<Alarm_DataModel> GetAlarmList()
{
var sql = #"EXEC dbo.listData #Criteria = '<Portal><Data Name=""Data"" Format=""Narrow"" Interval=""5m"">
<Where>
<Column Name=""Point"" Project=""XX"" Value=""XXXX"" Operator=""LIKE"" />
<Column Name=""Point"" Project=""YY"" Value=""YYYY"" Operator=""LIKE"" />
</Where>
</Data>
</Portal>', #StartDateTime = '12/28/2020',#EndDateTime = '12/29/2020'";
string connectionString = "Integrated Security=True;Data Source=XXXX;Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
using (var connection = new SqlConnection(connectionString))
{
return connection.Query<Alarm_DataModel>(sql).ToList();
}
}
[HttpGet]
public ActionResult Index()
{
var alarmList = GetAlarmList();
return View(alarmList);
}
}
Related
I'm trying to create login page using react front-end and ASP .NET core Back-end.
SO while a user login to the system I have to use the query like
Select * from UserLogin where email="asbhf#gmail.com"
so that my API URL should be
https://localhost:44383/api/UserLogins?email="asbhf#gmail.com"
So that, I tried this code in my UserLoginController.cs
// GET: api/UserLogins?email="asd#gmail.com"
public async Task<IActionResult> GetUserLogin([FromRoute] string email)
{
if (email == null)
{
return NotFound();
}
string query = "SELECT * FROM UserLogin WHERE email = #email";
var UserLogin = await _context.UserLogin
.FromSql(query, email)
.Include(d => d.Username)
.AsNoTracking()
.FirstOrDefaultAsync();
if (UserLogin == null)
{
return NotFound();
}
return Ok(UserLogin);
}
but, It won't print any out put as I expect. Could you please give me any hint to solve my issue.
Firstly , The Include method specifies the related objects to include in the query results. It can be used to retrieve some information from the database and also want to include related entities , not specify the fields to return. For more details , you could refer to here.
Secondly , there are some errors in your data query section , try the following modification:
string query = $"SELECT Username FROM UserLogin WHERE email = #email";
var p1 = new SqlParameter("#email", email);
var UserLogin = await _context.UserLogin
.FromSql(query, p1)
.AsNoTracking()
.FirstOrDefaultAsync();
You could take a look at Executing Raw SQL Queries for the usage of FromSql.
I refer write sql query for entityframework
So my working code is
var UserLogin = _context.UserLogin.Where(c => c.Email == email);
I am trying to perform a SELECT on the M1lhao table of Sorteio entity (database).
I don't want to go the traditional "string query" or AddWithParameter() way, i wanted to use the MVC4 EF5 available methods.
The following code passes the entire Table to the View, that i can do a foreach in the View and all works fine. What i am looking for is how can i do a SQL query, so i can pass only the element(s) i want, sorted DESCending (for example), obviously on a List and obeying the Model that the View expects.
Essentially i want a replacement for (i tried variants too, db.Milhao, etc):
var data = db.Database.ExecuteSqlCommand("SELECT * From M1lhao WHERE DrawID = {0}", id);
The problem with Find() is that it only searches primary keys.
The complete code:
public class M1lhaoController : Controller
{
private Sorteio db = new Sorteio();
public ActionResult Index(int id = 1)
{
var data = db.Database.ExecuteSqlCommand("SELECT * From M1lhao WHERE DrawID = {0}", id); // the variable data comes as -1
M1lhao m1lhao = db.M1lhao.Find(id);
if (m1lhao == null)
{
return HttpNotFound();
}
return View(db.M1lhao.ToList());
}
}
Thank you.
You can try as shown below.
var data = db.M1lhao.Where(m=>m.DrawID == id).Select(p=>p);
You can learn more about Method-Based Query Syntax : Projection
I have an installer which installs a database. The database is created alongside some logins. To create the logins I am using the master database in SqlString elements. Access to the master database is only granted to users who have very high privileges on the SQL server. Oftentimes the installation is aborted because a SQL string designated for the master database cannot be executed due the lack of rights.
I want to edit my installer, so that when a SqlString element cannot be executed, the SQL part of the installation shall be skipped. After the installation has taken place I want the user to be able to execute the SQL statements herself. Every SQL action taken by my installer is stored in SqlString elements. The SqlString elements contain a lot of properties which get replaced during the installation. I want to extract the content of all edited SqlString elements into one sql file stored in the user directory.
I guess I'll have to write a customaction which takes place after the sqlextension has substituted the properties. And then I'll have to access these altered strings. Is there any way I can do this?
Example SqlString element:
<sql:SqlDatabase Id="MasterDB" Server="[SQLSERVER_SERVER]" Instance="[SQLSERVER_INSTANCENAME]" Database="master" />
<sql:SqlString
SqlDb="MasterDB"
Id="CreateNetworkServiceAccount"
ExecuteOnInstall="yes"
ContinueOnError="no"
SQL="IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = N'{[WIX_ACCOUNT_NETWORKSERVICE]}')
CREATE LOGIN [\[]{[WIX_ACCOUNT_NETWORKSERVICE]}[\]] FROM WINDOWS WITH DEFAULT_DATABASE=[\[]master[\]]"
Sequence="101"/>
Example of the sql file I'd like to have after the SqlStrings have failed:
USE master;
IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = N'NT AUTHORITY\Network Service')
CREATE LOGIN [NT AUTHORITY\Network Service] FROM WINDOWS WITH DEFAULT_DATABASE=[master]
I have solved this problem with a rather odd solution. I have written a CustomAction which extracts the String elements from the SqlString table and then replaces the formatted fields with the appropriate Properties stored in the session. To have access to the session variable, the CustomAction has to be executed as immediate. I've scheduled it before InstallFinalize to be given access to the PersonalFolder property. With this property I am able to store a Sql script generated by the entries in the SqlScript table in the users Documents directory. To account for different databases in the Installation, I have included a lookup in the SqlDatabase table.
Here is the code to the CustomAction:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
using System.IO;
using System.Text.RegularExpressions;
namespace SaveSqlStrings
{
public class CustomActions
{
[CustomAction]
public static ActionResult SaveSqlStrings(Session session)
{
StringBuilder sqlStrings = new StringBuilder();
Database db = session.Database;
View view = db.OpenView("SELECT * FROM `SqlString`");
IList<string> SqlStringElements = db.ExecuteStringQuery("SELECT `String` FROM `SqlString`");
Regex bracketedProperties = new Regex(#"\[(\b[A-Z_]*\b)\]");
Regex formattedProperties = new Regex(#"{\[(\b[A-Z_]*\b)\]}");
Regex openeningSquareBrackets = new Regex(#"\[\\\[\]");
Regex closingSquareBrackets = new Regex(#"\[\\\]\]");
string sqlDb_ = "";
string sqlString = "";
string Database = "";
foreach (string dbString in SqlStringElements)
{
sqlDb_ = (string)db.ExecuteScalar("SELECT `SqlDb_` FROM `SqlString` WHERE `String` ='{0}'",dbString);
sqlString = (string)db.ExecuteScalar("SELECT `SQL` FROM `SqlString` WHERE `String` ='{0}'",dbString);
view.Close();
view = db.OpenView("SELECT * FROM `SqlDatabase`");
Database = (string)db.ExecuteScalar("SELECT `Database` from `SqlDatabase` WHERE `SqlDb`='{0}'", sqlDb_);
if(bracketedProperties.IsMatch(Database))
{
Database = bracketedProperties.Match(Database).Groups[1].Value;
Database = session[Database];
}
if (openeningSquareBrackets.IsMatch(sqlString))
sqlString = openeningSquareBrackets.Replace(sqlString, "[");
if (closingSquareBrackets.IsMatch(sqlString))
sqlString = closingSquareBrackets.Replace(sqlString, "]");
if(formattedProperties.IsMatch(sqlString))
{
string propertyName = formattedProperties.Match(sqlString).Groups[1].Value;
string propertyValue = session[propertyName];
sqlString = formattedProperties.Replace(sqlString, propertyValue);
}
sqlStrings.AppendLine(String.Format("use {0}",Database));
sqlStrings.AppendLine(sqlString);
}
string home = session["PersonalFolder"];
string sqlPath = string.Concat(home, #"Script.sql");
try
{
File.WriteAllText(sqlPath, sqlStrings.ToString());
}
catch (Exception ex)
{
session["FailedTowrite"] = sqlPath;
}
view.Close();
db.Close();
return ActionResult.Success;
}
}
}
My company has 2 departments which sell air tickets to different categories of customers.
the two departments' ticket database are different but the tables inside them are identical.
I want to in insert row of data or manipulate data according to departmentID. In the old time, I can use a variable, departmentID, to determine which department and connect to the right database. And since tables structure are identical, the rest of the code can be shared.
example: SQLstr = ".... from eAirsTable " + departmentID + " where ..."
But now I'm DataContext and I have no idea how to do it.
public class eAirs_OrderManager : IOrderInterface
{
public void Insert_OrderDB( Login _login)
{
if (_login.departmentID=="Orange")
{
OrderDB_testDataContext OrderDBDC = new OrderDB_testDataContext();
}
if (_login.departmentID=="Tristar")
{
OrderDBDataContext OrderDBDC = new OrderDBDataContext();
}
OrderDBDC.... **<--cannot be done**
}
public void Insert_Member_simple
{
...
}
}
Please advise.
As the underlying schema for for both the DB are same create only one data context with two different set of connection strings.
if (_login.departmentID=="Orange")
{
OrderDBDataContext OrderDBDC = new OrderDBDataContext("ConnectionString 1");
}
if (_login.departmentID=="Tristar")
{
OrderDBDataContext OrderDBDC = new OrderDBDataContext("ConnectionString 2c");
}
Create a different connection string for each department, and use that to create the different contexts.
OrderDBDataContext OrderDBDC = new OrderDBDataContext(New DBConnection("DepartmentConnectionStringName"));
I´m using the LinqDataSource to populate a grid. But now I need the SQL query that the LinqDataSource generates, to pass around throught methods (no, I can't modify the methods to not need a SQL query).
Is there a way to obtain the generated SQL query from a instantiated and configured LinqDataSource?
Hope this helps.
using the function below will return a SqlQueryText
you can rebuild the query from that object.
to get the sql text you can use use the .Text Property
to get the passed
parameters you can use the .Params property
public static SqlQueryText GetFullQueryInfo(DataContext dataContext, IQueryable query)
{
DbCommand dbCommand = dataContext.GetCommand(query);
var result = new SqlQueryText();
result.Text = dbCommand.CommandText;
int nParams = dbCommand.Parameters.Count;
result.Params = new ParameterText[nParams];
for (int j = 0; j < nParams; j++)
{
var param = new ParameterText();
DbParameter pInfo = dbCommand.Parameters[j];
param.Name = pInfo.ParameterName;
param.SqlType = pInfo.DbType.ToString();
object paramValue = pInfo.Value;
if (paramValue == null)
{
param.Value = null;
}
else
{
param.Value = pInfo.Value.ToString();
}
result.Params[j] = param;
}
return result;
}
here is an example
var results = db.Medias.Where(somepredicatehere);
ClassThatHasThisMethod.GetFullQueryInfo(yourdatacontexthere, results);
EDIT:
Sorry forgot to include the SqlQueryText data structures
public struct SqlQueryText
{
public ParameterText[] Params;
public string Text;
}
public struct ParameterText
{
public string Name;
public string SqlType;
public string Value;
}
You can run SQL Profiler while running your application and that should give it to you.
Take a look at LinqPad for debugging and to understand how it works. But if you want it at run-time, I think you're out of luck.
The Sql will only be generated by the Linq to Sql infrastructure at runtime.
I think there are some tools to see generated Sql in the debugger, but if you don't plan to use linq to generate your Sql dynamicaly, shouldn't you probably look for a simple Sql designer ?
I Found a Linq To Sql Debug visualizer on Scottgu's blog.