I am using Entity Framework ExecuteStoreQuery to call a MySql stored procedure with output parameter. The store procedure also returns a result set.
I get the following error.
OUT or INOUT argument 2 for routine mydb.mysp is not a variable or NEW pseudo-variable in BEFORE trigger
What is wrong? Is there a better way to call MySql sp with mix of out params and resultset?
Here is the code
var outParam = new MySqlParameter();
outParam.ParameterName = "totalRowsOut";
outParam.MySqlDbType = MySqlDbType.Int64;
outParam.Direction = ParameterDirection.Output;
var data = db.ExecuteStoreQuery<PropertySearchResultRow>
(
"call mysp(#input1, #totalRowsOut);",
new MySqlParameter("input1", 10),
outParam
);
var result = data.ToList();
int totalRowsCount = (int)outParam.Value;
Finally ended up using the ado.net MySqlCommand to call the stored proc and get the out value. Mysql and EF integration is bad.
You just need to specify the name of the stored procedure and pass in the parameters. Something like this
var data = db.ExecuteStoreQuery<PropertySearchResultRow>
(
"mysp",new MySqlParameter("input1", 10),outParam
);
Here is my sample code working with VS2012 and Mysql connector 6.8.3 hope this helps someone
public static IList<MyClass> GetCustOrderHist(string someParameter)
{
IList<MyClass> data = ((IObjectContextAdapter)TestDashboardEntities).ObjectContext.ExecuteStoreQuery<MyClass>("CALL CustOrderHist({0});", someParameter).ToList();
return data;
}
public class MyClass
{
public string ProductName { get; set; }
public int TOTAL { get; set; }
}
Related
I'm trying to set up an API for a system I'm working on, but the LINQ seems to not grab the parameters.
A bit of background: During covid I've been working with a local business owner to develop an info system for his business. So far, everything has been kept in the browser, but now we want to create a small windows form application the users can download instead of using the browser. The application will be much smaller in scope than the full site, but I don't want the SQL connection in the form.
So I guess my first question is, am I being overly cautious at not wanting the SQL connector in the client and wanting them to connect to the database, via an API, or is it safe enough to add the connection and calls directly in the application (I know how to do this, it's the API part I can't figure out). I'm thinking about it from a security point of view - would the users be able to find the connection and potentially do harm to my database by having it straight in the application or is it safe there?
If using API calls is the proper way to go, that leads me to my second question. How do I configure it properly?
This is my table (I've been following the Microsoft ToDoItems tutorials):
CREATE TABLE [dbo].[TodoItems] (
[Id] [int] identity(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[IsComplete] [bit] NULL,
[Secret] [nvarchar](10) NULL
) ON [PRIMARY]
GO
My test form has a single button, which when pressed calls this method:
static async Task RunAsync()
{
// Update port # in the following line.
client.BaseAddress = new Uri("https://localhost:7217/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// Create a new product
TodoItem product = new TodoItem
{
Name = "Gizmo",
IsComplete = false,
Secret = "false"
};
var url = await CreateProductAsync(product);
Console.WriteLine($"Created at {url}");
// Get the product
product = await GetProductAsync(url.PathAndQuery);
ShowProduct(product);
// Update the product
Console.WriteLine("Updating IsCompleted...");
product.IsComplete = true;
await UpdateProductAsync(product);
// Get the updated product
product = await GetProductAsync(url.PathAndQuery);
ShowProduct(product);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
My ToDoItem class looks like this:
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
public string Secret { get; set; }
}
My first issue is creating the ToDoItem. This method should do the trick:
static async Task<Uri> CreateProductAsync(TodoItem product)
{
HttpResponseMessage response = await client.PostAsJsonAsync(
"api/todoitems", product);
response.EnsureSuccessStatusCode();
// return URI of the created resource.
return response.Headers.Location;
}
However, when I run the method my API logs this error and nothing is posted to the database:
Executed DbCommand (46ms) [Parameters=[#p0='?' (DbType = Boolean), #p1='?' (Size = 4000), #p2='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
INSERT INTO [TodoItems] ([IsComplete], [Name], [Secret])
VALUES (#p0, #p1, #p2);
SELECT [Id]
FROM [TodoItems]
WHERE ##ROWCOUNT = 1 AND [Id] = scope_identity();
The way I read this, and I might be wrong, the method CreateProductAsync (which gets a product with the values "Gizmo", false and "false") simply doesn't transfer the values to the API.
For reference, my API ToDoContext class look like this:
public class TodoContext : DbContext
{
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
{
}
public DbSet<TodoItem> TodoItems { get; set; } = null!;
}
Do I need to add something to this class? I'm wholly unfamiliar with both API and LINQ, but I did figure out that changing the table name to ToDoItems made the connection for me on its own.
I have 3 layers in Asp.Net Web API. The controller layer, service layer and repository layer implemented using EF.
I am new to unit testing and have a simple function that gets a person by their id in the database and nothing else.
Basically the service layer calls
Unit_Work.Person_Repository.GetPersonByID(id);
and the Repository does this:
return context.chapters.Where(p=>p.chapterID==id).SingleOrDefault();
What kind of Unit Test would i write on this.
should i use the database or a mock implementation.
I thought of using Sql Server Compact populating it with a mock person and then trying to get that person by ID is this correct.?
Thanks in advance to all those that answer.
If you are using entity framework you can't unit test your data access layer.
Solution provided by Erik Alsmyr is very wrong!
Look here why - What's the point of in memory IDbSet?.
When you use in memory db sets you are running Linq to Objects. When you use EF's DbContext your Linq is converted to SQL. Those are two different things!
It is very easy to write code that will work with in memory db set (all your unit tests will pass and you will be happy) just to notice runtime error first time you try to hit database.
Let's modify this code a bit. I don't think FULLNAME should have setter if we are using FIRSTNAME, LASTNAME. It should be calculated from FIRSTNAME and LASTNAME.
class User
{
public string FIRSTNAME { get; set; }
public string LASTNAME { get; set; }
public string FULLNAME
{
get { return string.Format("{0}, {1}", LASTNAME, FIRSTNAME }
}
User(string firstName, string lastName)
{
this.FIRSTNAME = firstName;
this.LASTNAME = lastName;
}
}
Now you can write test like this and it will pass (of course after you implement it in controller)
public IMyEntities GetRepoWithUsers(params User[] users)
{
var inMemoryUsers = new InMemoryDbSet<User>();
var mockData = new Mock<IMyEntities>();
mockData.Setup(m => m.Users).Returns(inMemoryUsers);
return mockData.Object;
}
[Test]
public void GetUserByFullname()
{
var ankaArne = new User("Arne", "Anka");
var bjornBertil = new User("Bertil", "Björn");
var repo = GetRepoWithUsers(ankaArne, bjornBertil);
var usersController = new UsersController(repo);
var found = usersController.GetUser("Anka, Arne");
Assert.NotNull(found);
Assert.AreEqual("Anka", found.LASTNAME);
Assert.AreEqual("Arne", found.FIRSTNAME);
}
But when you run it against 'real' DbContext and 'real' DbSet it will throw because you can't do Linq queries on calculated properties. Only on those that are mapped to database columns. So what's the point of that test?
You can use the xml/ .csv file for data. ie, you need to fetch the ID, chapter details from the xml file inside the unit test project. Then you have to pass the id as the parameter then check the return values with the data fetch from xml file. if you dont understand let me know. You cn create unit test project by add-new project options. then on vs2010 ther r options to add xml file for fetching the data to be tested.
your 3rd question is also correct. u cn populate the data from database and check the data with the return value
I recommend mocking and injecting an Entity framework context into your repository.
We do this using something similar to http://nuget.org/packages/FakeDbSet/
Then our unit tests look like this:
[TestFixture]
class UsersControllerTester
{
private Mock<IMyEntities> mockData = null;
[SetUp]
public void Setup()
{
// Create fake data
var inMemoryUsers = new InMemoryDbSet<User>();
inMemoryUsers.Add(new User { ID = 1, FIRSTNAME = "Arne", LASTNAME = "Anka", EMAIL = "arne.anka#email.com", FULLNAME = "Anka, Arne", USERNAME = "arne.anka" });
inMemoryUsers.Add(new User { ID = 2, FIRSTNAME = "Bertil", LASTNAME = "Björn", EMAIL = "bertil.bjorn#email.com", FULLNAME = "Björn, Bertil", USERNAME = "bertil.bjorn" });
inMemoryUsers.Add(new User { ID = 3, FIRSTNAME = "Carl", LASTNAME = "Cool", EMAIL = "carl.cool#email.com", FULLNAME = "Cool, Carl", USERNAME = "carl.cool" });
inMemoryUsers.Add(new User { ID = 4, FIRSTNAME = "David", LASTNAME = "Dûsk", EMAIL = "david.dusk#email.com", FULLNAME = "Dûsk, David", USERNAME = "david.dusk" });
// Create mock unit of work
mockData = new Mock<IMyEntities>();
mockData.Setup(m => m.Users).Returns(inMemoryUsers);
}
[Test]
public void GetUser()
{
// Test
var usersController = new UsersController(mockData.Object);
// Invoke
User user1 = usersController.GetUser("1");
// Assert
Assert.NotNull(user1);
Assert.AreEqual(1, user1.ID);
Assert.AreEqual("Anka", user1.LASTNAME);
}
Hi guys I need some help with this dataconduit. Its written by my tutor and we used it on a web development project. I am trying to use it for a windows forms application to connect to the database but i get the following error:
An attempt to attach an auto-named database for file C:\Users.... failed. A database with the same name exists, or specified file cannot be opened, or it is located on UNC share.
The data conduit works indeed if i use it on a asp.net website but not on a windows forms
i did try to researched but no luck.
I am just testing it with two text box and a save button
thank you
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.SqlClient;
using System.Data.OleDb;
using System.Data;
///This class uses the ado.net sql classes to provide a connection to an sql server database.
///it is free for use by anybody so long as you give credit to the original author i.e me
///Matthew Dean mjdean#dmu.ac.uk De Montfort University 2011
//you will need to modify the name of the namespace to suit your own program.
namespace MyClassLibrary
{
public class clsDataConduit
{
//connection object used to connect to the database
SqlConnection connectionToDB = new SqlConnection();
//data adapter used to transfer data to and from the database
SqlDataAdapter dataChannel = new SqlDataAdapter();
//ado.net class for building the sql commands
SqlCommandBuilder commandBuilder = new SqlCommandBuilder();
//stores a list of all of the sql parameters
List<SqlParameter> SQLParams = new List<SqlParameter>();
//data table used to store the results of the stored procedure
DataTable queryResults = new DataTable();
//data row used to store the data for a new record
DataRow newRecord;
//string variable used to store the connection string
private string connectionString;
public clsDataConduit()
{
//this is the constructor for the class
//you will need to modify this to suit your own database name and folder structure
//
//variable to store the patth to the database
string DbPath;
//variable to store the partial path and file name of your database
//modify this line to suit your own needs
string DatabaseName = "\\MyDatabase\\NamesData.mdf";
//set the DbPath concatenating the name of your database
DbPath = GetParentPath() + DatabaseName;
//build up the connection string for the sql server database
connectionString = "Data Source=.\\SQLEXPRESS;AttachDbFilename=" + DbPath + ";Integrated Security=True;User Instance=True";
}
private string GetParentPath()
///this function returns the path to the parent folder of the solution
{
//get the folder for the project
string DbPath = System.AppDomain.CurrentDomain.BaseDirectory;
//variable to store the position of the \\ characters
int Posn;
//loop through the path twice
for (int Counter = 0; Counter != 2; Counter++)
{
//find the right most instance of \\
Posn = DbPath.LastIndexOf("\\");
//split the path at that point
DbPath = DbPath.Substring(0, Posn);
//do it one more time
}
//return the new path
return DbPath;
}
public void AddParameter(string ParamName, string ParamValue)
///public method allowing the addition of an sql parameter to the list of parameters
///it accepts two parameters the name of the parameter and its value
{
//create a new instance of the sql parameter object
SqlParameter AParam = new SqlParameter(ParamName, ParamValue);
//add the parameter to the list
SQLParams.Add(AParam);
}
public void Execute(string SProcName)
{
///public method used to execute the named stored procedure
///accepts one parameter which is the name of the stored procedure to use
//open the stored procedure
//initialise the connection to the database
connectionToDB = new SqlConnection(connectionString);
//open the database
connectionToDB.Open();
//initialise the command builder for this connection
SqlCommand dataCommand = new SqlCommand(SProcName, connectionToDB);
//add the parameters to the command builder
//loop through each parameter
for (int Counter = 0; Counter < SQLParams.Count; Counter += 1)
{
//add it to the command builder
dataCommand.Parameters.Add(SQLParams[Counter]);
}
//set the command type as stored procedure
dataCommand.CommandType = CommandType.StoredProcedure;
//initialise the data adapter
dataChannel = new SqlDataAdapter(SProcName, connectionToDB);
//set the select command property for the data adapter
dataChannel.SelectCommand = dataCommand;
//use the copmmand builder to generate the sql insert delete etc
commandBuilder = new SqlCommandBuilder(dataChannel);
//fill the data adapter
dataChannel.Fill(queryResults);
//get the structure of a single record
newRecord = queryResults.NewRow();
//close the connection
connectionToDB.Close();
}
public void WriteToDatabase()
//void method that updates changes to the data adapter thus changing the database
{
//update any changes
dataChannel.Update(queryResults);
}
public DataRow NewRecord
///this method provides access to the new record as a single data row
{
get
{
//return the blank data row
return newRecord;
}
}
public void RemoveRecord(int Index)
//void method that removes a record at a specified index in the query results
{
//remove the record
queryResults.Rows[Index].Delete();
}
public void AddToDataTable()
//void method that adds the new record to the table data
{
//add the new record to the table
queryResults.Rows.Add(newRecord);
//re initialise the new record
newRecord = queryResults.NewRow();
}
public int Count
//property that returns the count of records in the query results
{
get
{
//return the count of the query results
return queryResults.Rows.Count;
}
}
public DataTable QueryResults
//public property that provides access to the query results
{
get
{
//return the query results
return queryResults;
}
set
{
//set the query results
queryResults = value;
}
}
}
}
this is the code for my name class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyClassLibrary
{
public class clsName
{
private string firstName;
private string lastName;
public string FirstName
{
get
{
return firstName;
}
set
{
firstName = value;
}
}
public string LastName
{
get
{
return lastName;
}
set
{
lastName = value;
}
}
public void Save()
{
clsDataConduit Names = new clsDataConduit();
Names.Execute("sproc_tblNames_GetAll");
Names.NewRecord["FirstName"] = firstName;
Names.NewRecord["LastName"] = lastName;
Names.AddToDataTable();
Names.WriteToDatabase();
}
}
}
thank you guys but i finally managed to make it work. i am not sure if it is the right way to do it but what u have done is i have commented out the public clsDataConduit() {} method and before this method i have modified the connection string adding the full path of my database as follow: private string connectionString = (#"Data Source=.\SQLEXPRESS;AttachDbFilename=C:\Users...\NamesData.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True");
I am very new to ASP.NET so I apologize for the naive question but I was just wondering how do I retrieve the data from a stored procedure I am calling from within ASP.NET .The stored procedure is supposed to return a single row and I want to retrieve the record fields returned.
So this is what I have come up with so far
The stored Procedure
ALTER PROCEDURE dbo.StoredProcedure6
#LoginName varchar(50)
AS
SELECT username ,Password FROM dbo.Users
Where username = #LoginName
RETURN
The code to get access to the specific record within the asp.net.cs file
var user = dbcontext.returnuserdetails(txtEmailAddress.Text);
where returnuserdetails is the function I added via the model browser in Visual studio 2010
Now the question is how do I get and store the values of the username and password which are returned ?
I am working in ASP.NET 4.0 if it helps.
Thanks
if you are in 4.0 you can easily just use the LINQ to SQL stuff, no need for stored procedure here.
private void GetUser(string emailAddress){
using(DataContext dbcontext = new DataContext()){
var AppData.user = dbcontext.users
.Select(u => u.email_address == emailAddress).SingleOrDefault();
// access entity properties as needed
// user.email_address, user.first_name, etc..
}
}
That said you aren't really stating what it is you are trying to do with the user entity and passwords should NEVER be stored as plain text.
if you are forced to use Stored Procedures then the return in LINQ-to-SQL will always be a set.
ALTER PROCEDURE dbo.ReturnUserDetails
#LoginName varchar(50)
AS
SELECT * -- Get whole row so you have all info that is possibly needed
FROM dbo.Users
Where username = #LoginName
RETURN
C# Code
private void GetUser(string userName){
using(DataContext dbcontext = new DataContext()){
var user = dbcontext.ReturnUserDetails(userName).SingleOrDefault();
// access entity properties as needed
string userName = user.username;
var password = user.Password;
}
}
Use SqlDataReader Class
Edit:
This is a code sample from the link, now the difference between this sample and your case is that you should specify you command type as StoredProcedure.
If it still doesn't help let me know.
private static void ReadOrderData(string connectionString)
{
string queryString =
"SELECT OrderID, CustomerID FROM dbo.Orders;";
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
Console.WriteLine(String.Format("{0}, {1}",
reader[0], reader[1]));
}
// Call Close when done reading.
reader.Close();
}
}
I have DropDownList displays all server present in my network
I want to populate the databse names when i changed it into another dropdown
I want a LINQ query to get this.
See the question here on how to get the list of databases available to the logged-in user:
SELECT name
FROM sys.sysdatabases
WHERE HAS_DBACCESS(name) = 1
Implementing this into LINQ requires you to publish the sysdatabases view into your own view. I would recommend creating a view for the job in one of the databases:
CREATE VIEW [dbo].[vewDatabases]
AS
SELECT name
FROM sys.sysdatabases
WHERE HAS_DBACCESS(name) = 1
GO
Add this to your LINQ data model, and you will be able to select from it like so [c#]:
var databaseNames = serverDataContext.vewDatabases.Select(a => a.name);
You can then populate the DropDownList with the contents of databaseNames.
I don't think this is a job for Linq2SQL (though I'm not overly familiar with it).
Basically, I think you can get the results you want by looking at one of the SYS_ tables, or a SP_ stored procedure. Can't remember which - been a while since I did anything with SQL Server.
Try code below:
protected void ddList_SelectedIndexChanged(object sender, EventArgs e)
{
PopulateDatabaseDropDown(ddList.SelectedItem.Text);
}
private void PopulateDatabaseDropDown(string server)
{
ddDatabase.DataSource = DatabaseName(server);
ddDatabase.DataValueField = "DatabaseId";
ddDatabase.DataTextField = "Name";
ddDatabase.DataBind();
}
private DataBase[] DatabaseName(string serverName)
{
List<DataBase> data = new List<DataBase>();
System.Data.Linq.DataContext db = new System.Data.Linq.DataContext("Data Source="+serverName+";Initial Catalog=master;Integrated Security=SSPI");
var dbName = db.ExecuteQuery<DataBase>("select database_id, [name] from sys.databases").AsEnumerable();
foreach (var item in dbName)
data.Add(new DataBase() { DatabaseId = item.DatabaseId, Name = item.Name });
return data.ToArray();
}
}
public class DataBase
{
public string Name { get; set; }
public int DatabaseId { get; set; }
}