sp_getProcedureColumns - advantage-database-server

I have tried to pass in the proc name into procedureNamePattern and the procedure seems to return all the metadata for all the procedures. I pass in a empty string and I get the same results.
The following is the code used:
using (AdsCommand command = new AdsCommand { CommandText = "sp_getProcedureColumns",
Connection = connection,
CommandType = CommandType.StoredProcedure })
{
AdsParameter param = new AdsParameter("#procedureNamePattern", DbType.String) {
Direction = ParameterDirection.Input, Value = originalProcedureName };
command.Parameters.Add(param);
AdsParameter param0 = new AdsParameter("#catalog", DbType.String) {
Direction = ParameterDirection.Input, Value = null };
command.Parameters.Add(param0);
AdsParameter param1 = new AdsParameter("#schemaPattern", DbType.String) {
Direction = ParameterDirection.Input, Value = null };
command.Parameters.Add(param1);
AdsParameter param2 = new AdsParameter("#columnNamePattern", DbType.String) {
Direction = ParameterDirection.Input, Value = null };
command.Parameters.Add(param2);

With stored procedures, you can use the DeriveParameters method. That might make it simpler. The following is an example:
AdsCommand cmd = conn.CreateCommand();
cmd.CommandText = "sp_getProcedureColumns";
cmd.CommandType = CommandType.StoredProcedure;
cmd.DeriveParameters();
cmd.Parameters["ProcedureNamePattern"].Value = "MyProcName";
AdsDataReader rdr = cmd.ExecuteReader();
If you do not call the DeriveParameters method, then the underlying SQL statement that is generated is produced directly from the provided parameters in the given order. That means you would need to provide the parameters in the order to match the procedure definition. In this case, the ProcedureNamePattern parameter needs to be 3rd. If you change the order of the cmd.Parameter.Add() calls, then your original example should work.

Related

Retrieve data in asp.net MVC from complex stored procedure which return multiple actual table

User Defined Stored Procedure which returns multiple actual table as result set.
CREATE PROCEDURE uspDemo(
#UserID BIGINT=0,
#IsAdmin bit=0,
#Title varchar(120)=''
)AS
BEGIN
------Retrieve Posts------
SELECT * FROM tblPost AS MP INNER JOIN tblUserProfile AS UP ON UP.ID=MP.UserID
WHERE UP.ID=#UserID AND ((#IsAdmin=0 AND MP.IsDeleted=0 AND MP.IsApproved=1)OR (#IsAdmin=1 OR MP.IsDeleted=0 OR MP.IsApproved=1))
----- Retrieve Tags------
SELECT * FROM tblTagMasters AS MT INNER JOIN tblPostTags AS MP ON MT.TagID= MP.TagID
--------Retrieve User likes-----
SELECT * FROM tblUserLikes AS UV INNER JOIN tblPost AS MP ON MP.PostId=UV.PostId
END
I want to convert into list format of all returned actual table from stored procedure according to model in asp.net MVC.
public List<PostView> GetPosts(int userID = 0, string s = "")
{
IEnumerable<PostView> query = null;
using (var db = new MVCDatabase())
{
var cmd = db.Database.Connection.CreateCommand();
cmd.CommandText = "[dbo].[uspDemo]";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#UserID", userID));
cmd.Parameters.Add(new SqlParameter("#IsAdmin", 0));
cmd.Parameters.Add(new SqlParameter("#Title", s));
try
{
db.Database.Connection.Open();
using (var result = cmd.ExecuteReader())
{
var Posts = ((IObjectContextAdapter)db).ObjectContext.Translate<PostView>(result).ToList();
result.NextResult();
var tags = ((IObjectContextAdapter)db).ObjectContext.Translate<TagView>(result).ToList();
result.NextResult();
var uservotes = ((IObjectContextAdapter)db).ObjectContext.Translate<UserVoteView>(result).ToList();
Posts.ForEach(z =>
{
z.TagMaster = tags.Where(x => x.PostId == z.PostId).ToList();
z.UserLike = uservotes.Where(x => x.PostId == z.PostId).ToList();
});
query = Posts;
}
}
catch (Exception ex)
{
MSError.Trace(ex);
}
finally
{
db.Database.Connection.Close();
cmd.Dispose();
}
return query.ToList();
}
}
Throwing an ArgumentNullException
Help me to find out the solution.
Here is a demo how to do it. Using System.data and System.Linq you can do below.
var cmd = db.Database.Connection.CreateCommand();
cmd.CommandText = "[dbo].[uspDemo]";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#UserID", userID));
cmd.Parameters.Add(new SqlParameter("#IsAdmin", 0));
cmd.Parameters.Add(new SqlParameter("#Title", s));
SqlDataAdapter da = new SqlDataAdapter(); //adapter
DataSet ds = new DataSet(); //dataset
cmd.CommandType = CommandType.StoredProcedure;
da = new SqlDataAdapter(cmd);
da.Fill(ds); //fill dataset with multiple select
var Posts = (from DataRow row in ds.Tables[0].Rows //0 means 1st select
select new Posts //Posts model to map
{
test = row["test"].ToString(), //test is the column name from select
test1 = Convert.ToDecimal(row["test1"])
}).ToList();
var Tags = (from DataRow row in ds.Tables[1].Rows //1 means 2nd select
select new Tags //Tags model to map
{
test = row["test"].ToString(), //test is the column name from select
test1 = Convert.ToDecimal(row["test1"])
}).ToList();
var User = (from DataRow row in ds.Tables[2].Rows //2 means 3rd select
select new User //User model to map
{
test = row["test"].ToString(), //test is the column name from select
test1 = Convert.ToDecimal(row["test1"])
}).ToList();

SaveChanges not setting value, selecting existing value instead

When trying to commit a modified entry to the database, Entity Framework is selecting values for two properties as opposed to setting them.
The code to commit the entity to the database is as follows:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Till till)
{
if (ModelState.IsValid)
{
string username = User.Identity.Name.Substring(User.Identity.Name.LastIndexOf('\\') + 1);
till.ModifiedDate = DateTime.Now;
till.ModifiedByUid_FK = db.Users.Where(m => m.Name.ToLower() == username.ToLower()).First().UserUid_PK;
db.Entry(till).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index/" + (int)Session["siteid"]);
}
ViewBag.SiteUid_FK = new SelectList(db.Sites, "SiteUid_PK", "Name", till.SiteUid_FK);
ViewBag.ModifiedByUid_FK = new SelectList(db.Users, "UserUid_PK", "EHLogin", till.ModifiedByUid_FK);
ViewBag.CreatedByUid_FK = new SelectList(db.Users, "UserUid_PK", "EHLogin", till.CreatedByUid_FK);
return View(till);
}
When viewed in the profiler, it looks like this:
exec sp_executesql N'update [dbo].[Till]
set [NetworkId] = #0, [MID] = #1, [SiteUid_FK] = #2, [Location] = null, [MasterTillNo] = #3,
[IsMaster] = #4, [IsBackOffice] = #5, [IsCurrent] = #6, [ModifiedByUid_FK] = #7,
[CreatedByUid_FK] = #8, [ProcessFiles] = #9
where ([TillUid_PK] = #10)
select [ModifiedDate], [CreatedDate]
from [dbo].[Till]
where ##ROWCOUNT > 0 and [TillUid_PK] = #10',N'#0 varchar(50),#1 int,#2 int,#3 int,#4 bit,#5 bit,#6
bit,#7 int,#8 int,#9 bit,#10 int',
#0='',#1='',#2=4,#3=42,#4=1,#5=0,#6=1,#7=4,#8=1,#9=1,#10=699
I'm guessing there's some property of the field in the database that EF uses to determine this functionality but I can't figure out what it is. If anyone has any information that'd be fantastic :)
Edit 1
Using Ibrahim's answer below, results in relatively similar SQL:
exec sp_executesql N'declare #p int
update [dbo].[Till]
set #p = 0
where ([TillUid_PK] = #0)
select [ModifiedDate], [CreatedDate]
from [dbo].[Till]
where ##ROWCOUNT > 0 and [TillUid_PK] = #0',N'#0 int',#0=687
This typically happens when properties have the data annotation
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
or the fluent mapping
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed)
or the StoreGeneratedPattern is Computed in the edmx file.
I'd guess that there is a trigger or a default value on ModifiedDate and CreatedDate on the columns in the database. Maybe it's even useless to set them in your application code.
I have had bad luck with the following also:
db.Entry(entity).State = EntityState.Modified;
So I suggest trying to do the following:
//Get the existing Till
var existingTill = db.Tills.Find(till.TillId);
//Save changes for only changed properties
db.Entry(existingTill).CurrentValues.SetValues(till);
db.SaveChanges();

there's something wrong with my controller codes

My Controller
public ActionResult Index()
{
TechnicianFacade _oTechFacade = new TechnicianFacade();
Maintenance_.Models.IndexModel _oTechModel = new Maintenance_.Models.IndexModel();
IList<Maintenance_.Models.IndexModel> _otechList = new List<Maintenance_.Models.IndexModel>();
var tech = _oTechFacade.getTechnicians("", _oAppSetting.ConnectionString).ToArray();
foreach (var test in tech)
{
string fName = test.GetType().GetProperty("FIRSTNAME").GetValue(test, null).ToString();
_oTechModel.firstName = fName;
_otechList.Add(_oTechModel); <===
}
_oTechModel.fNameList = _otechList;
return View("Index", _oTechModel);
}
In my controller: index, can get all data object from my database. But if I have more than one data object in my database the: _otechList.Add(_otechModel) will overwrite the first entry with the newly added data, like for example lets just say we have 2 object data: (FIRST loop of foreach) _otechList.Add(_oTechModel) has a data of "FIRSTNAME" = "GEM" where count = 0, (SECOND loop of foreach) _otechList.Add(_oTechModel) has a data of "FIRSTNAME" = "DIAMOND" where count = 1, this time the value of count[0] became "FIRSTNAME" = "DIAMOND" as well. Is there something missing in my code or there's something wrong on it?
It will replace because same object value is changed, you need to instantiate object inside foreach loop.
Maintenance_.Models.IndexModel _oTechModel = new Maintenance_.Models.IndexModel();
like this:
foreach (var test in tech)
{
string fName = test.GetType().GetProperty("FIRSTNAME").GetValue(test, null).ToString();
Maintenance_.Models.IndexModel _oTechModel = new Maintenance_.Models.IndexModel();
_oTechModel.firstName = fName;
_otechList.Add(_oTechModel);
}
your object is global so every time same object is added in the list, instantiate it inside foreach loop so that every time new object is added in the list.

zf2 select columns from joined tables - how?

I feel like I must me missing something very simple. It's a very simple task, all I want to do is get something like:
SELECT * FROM lookup_items
JOIN lookup ON lookup_items.lookup_id = lookup.id
This returns all the columns for all the joined tables, in regular SQL. Here's my attempt in zf2:
$select = new Select();
$select->from('lookup_items');
$select->join('lookup', 'lookup_items.lookup_id = lookup.id');
The result set only includes the columns in 'lookup_items'. I've tried various ways to get the 'lookup' columns, including:
$select->columns(array('lookup_items.*', 'lookup.*'));
But they all just blow up. Surely there's a way to do this, and it's just so simple I'm missing it completely.
I thought a simple example would be avoid confusion, but here's more code:
class LookupItemsTable extends AbstractTableGateway
{
public function getList($resource)
{
$system_name = str_replace('*', '%', strtoupper($resource));
$joinTable = 'lookup';
$select = new Select();
$select->from($this->table);
$select->join($joinTable, "{$this->table}.lookup_id = {$joinTable}.id");
$where = array();
$where[] = "{$this->table}.enabled is true";
$where[] = "{$joinTable}.enabled is true";
$where[] = "UPPER({$joinTable}.system_name) ilike '{$system_name}'";
$select->where($where);
$sort[] = 'sort_order ASC';
$sort[] = 'value ASC';
$select->order($sort);
$rowset = $this->selectWith($select);
return $rowset;
}
}
Where:
$resource = $this->params()->fromRoute('resource', 'BUSINESS');
And $this->table is 'lookup_items'. Really all I want to do is get columns from both joined tables. I guess there's a zf2 way to just make a straight SQL statement without all the OO falderal, so I could just force it that way. But I'd rather work within the framework as much as possible.
Just change this line
$select->join('lookup', 'lookup_items.lookup_id = lookup.id');
to
$select->join('lookup', 'lookup_items.lookup_id = lookup.id', array('lookupcol1', 'lookupcol2');
Raj answer is the best one but it only works if you don't forget to add these fiels in your LookupItems model.
class LookupItems
{
// Your lookup_items fields here...
...
// And the added lookup fields here, the ones you add in the array
public $lookupcol1;
public $lookupcol2;
And in the exchangeArray method :
public function exchangeArray($data)
{
// .... your fields, and the new ones
$this->lookupcol1 = (! empty($data['lookupcol1'])) ? $data['lookupcol1'] : null;
$this->lookupcol2 = (! empty($data['lookupcol2'])) ? $data['lookupcol2'] : null;
}
I figured it out.
Added this:
$select->columns(array('*'));
And then this near the end:
$sql = new Sql($this->adapter);
$statement = $sql->prepareStatementForSqlObject($select);
$rowset = $statement->execute();
This returns the expected result, with the caveat that now my rows are returned as associative arrays instead of objects.
This is how you can create queries with join in zf2.
$resultSet = $this->select(function (Select $select) {
// omit the table name
//$select->from('foo');
$select->join('users', "users.id foo.createdby", 'firstname', '');
$select->order('id ASC');
// echo $select->getSqlString();// to print your query
});
$entities = array();
foreach ($resultSet as $row) {
$entity = new Entity\Foo();
$entity->setId($row->id)
->setFullname($row->fullname)
->setCaseid($row->caseid)
->setTestimonial($row->testimonial)
->setSortorder($row->sortorder)
->setActive($row->active)
->setCreated($row->created)
->setModified($row->modified)
->setFirstname($row->firstname)
->setCreatedby($row->createdby);
$entities[] = $entity;
}
return $entities;

assign nullable objects for returning IQueryable

I am returning IQueryable<Customer> to the other method for some querying operations. The return method looks like:
return from cust in _dbCustList
select new Customer
{
CustomerId = cust.Customer_Id,
FirstName= cust.First_Name,
LastName= cust.Last_Name,
DOB= cust.Date_Of_Birth,
LoginTime = cust.Login_Time ?? new TimeSpan(0, 0, 0);
};
In the above result, cust.Login_Time is nullable property.
When i try to query the above result, it throws an error:
Method 'System.TimeSpan GetTimeSpan(System.Nullable`1[System.TimeSpan])' has no supported translation to SQL.
How to solve this error?
I would query into an anonymous type and then map the result to your business object in-memory:
var q = from cust in _dbCustList
select new
{
cust.Customer_Id,
cust.First_Name,
cust.Last_Name,
cust.Date_Of_Birth,
cust.Login_Time
};
return from cust in q.AsEnumerable()
select new Customer
{
CustomerId = cust.Customer_Id,
FirstName= cust.First_Name,
LastName= cust.Last_Name,
DOB= cust.Date_Of_Birth,
LoginTime = cust.Login_Time ?? TimeSpan.Zero;
};
Why do you use the null- check?
When you remove the null check the written query gets translated into a SQL query and will be executed. Now you have the result you can do any magic you want...

Resources