ASMX Service Doesn't Always Return Data - c#

I am working with an ASMX service in ASP.NET/C#. My service returns proper data for some of my WebMethods, but not all. The interesting part is all of the WebMethods are very similar.
Here's one that always returns data:
[WebMethod]
public AccountItem[] GetAllAccounts()
{
AccountItem[] AccountItems = HttpContext.Current.Cache[AccountItemsCacheKey] as AccountItem[];
if (AccountItems == null)
{
List<AccountItem> items = new List<AccountItem>();
using (SqlManager sql = new SqlManager(SqlManager.GetSqlDbiConnectionString()))
{
using (SqlDataReader reader = sql.ExecuteReader("SELECT A.Account_Id, A.Customer_Id, C.Last_Name + ', ' + C.First_Name AS CustomerName, A.[Status], AT.Name AS AcctType, A.Employee_Id, A.Initial_Balance, A.Interest_Rate, '$'+CONVERT(varchar(50), A.Balance, 1) AS Balance FROM Account A JOIN Account_Type AT ON A.Account_Type_Id=AT.Account_Type_Id JOIN Customer C ON A.Customer_Id=C.Customer_Id WHERE [Status]=1"))
{
while (reader.Read())
{
AccountItem item = new AccountItem();
item.AccountId = (int)reader["Account_Id"];
item.CustomerId = (int)reader["Customer_Id"];
item.CustomerName = (string)reader["CustomerName"];
item.AccountStatus = (bool)reader["Status"];
item.AccountType = (string)reader["AcctType"];
item.InitialBalance = (decimal)reader["Initial_Balance"];
item.InterestRate = (decimal)reader["Interest_Rate"];
item.Balance = (string)reader["Balance"];
items.Add(item);
}
reader.Close();
}
}
HttpContext.Current.Cache.Add(AccountItemsCacheKey, items.ToArray(), null, DateTime.Now.AddMinutes(CacheDuration), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
return items.ToArray();
}
else
{
return AccountItems;
}
}
And here's one that never returns data:
[WebMethod]
public TransactionItem[] GetAllTransactions()
{
TransactionItem[] tranItems = HttpContext.Current.Cache[TransactionItemsCacheKey] as TransactionItem[];
if (tranItems == null)
{
List<TransactionItem> items = new List<TransactionItem>();
using (SqlManager sql = new SqlManager(SqlManager.GetSqlDbiConnectionString()))
{
using (SqlDataReader reader = sql.ExecuteReader("SELECT [Transaction_Id],[Account_Id],[Amount],[DateTime],[Comment],TT.[Name] AS [TransType],[Flagged],[Employee_Id],[Status] FROM [Transaction] T JOIN [Trans_Type] TT ON T.Trans_Type_Id=TT.Trans_Type_Id"))
{
while (reader.Read())
{
TransactionItem item = new TransactionItem();
item.TransactionId = (int)reader["Transaction_Id"];
item.AccountId = (int)reader["Account_Id"];
item.Amount = (decimal)reader["Amount"];
item.Timestamp = (DateTime)reader["DateTime"];
item.Comment = (string)reader["Comment"];
item.TransType = (string)reader["TransType"];
item.Flagged = (bool)reader["Flagged"];
item.EmployeeId = (int)reader["Employee_Id"];
item.Status = (bool)reader["Status"];
items.Add(item);
}
reader.Close();
}
}
HttpContext.Current.Cache.Add(TransactionItemsCacheKey, items.ToArray(), null, DateTime.Now.AddMinutes(CacheDuration), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
return items.ToArray();
}
else
{
return tranItems;
}
}
As you can see, they're almost identical. The SQL queries for both return a ton of records, but only the GetAllAccounts() WebMethod actually returns that data back.
This is how I'm displaying the data passed back from GetAllAccounts(), which works fine:
#{
Layout = "~/Shared/_Layout.cshtml";
Page.Title = "Accounts";
Page.Header = "BankSite Mobile - Accounts";
var svc = IntranetService.GetAllAccounts();
}
<div data-role="content">
<ul data-role="listview" data-inset="true" data-theme="c">
#foreach(var item in svc){
<li>
<h3>Account ##item.AccountId.ToString() (#item.AccountType)</h3>
<p>Customer: #item.CustomerName</p>
<p>Account Balance: #item.Balance</p>
</li>
}
</ul>
</div>
Yet, this doesn't work fine, though it's the almost the exact same code:
#{
Layout = "~/Shared/_Layout.cshtml";
Page.Title = "Customers";
Page.Header = "BankSite Mobile - Customers";
var svc = IntranetService.GetAllCustomers();
}
<div data-role="content">
<ul data-role="listview" data-inset="true" data-theme="c">
#foreach(var item in svc){
<li>
<h3>Account ##item.CustomerId.ToString() (#item.CustomerId)</h3>
<p>Customer: #item.CustomerId</p>
<p>Account Balance: #item.CustomerId</p>
</li>
}
</ul>
</div>
...So basically I'm baffled. I don't understand why the data isn't being returned as expected from the non-working WebMethod (GetAllCustomers()). What am I missing?

If you disable loading stuff from the cache, would both methods always succeed to return expected result set? I would try that first, my gut feeling is that something funky with the cache (i.e expires before your method returns). Then go from there.

Try to isolate the problem to the web service by accessing the web service directly in a web browser. Also, if possible, use SQL Server Profiler to make sure the web method is querying the database.
If it is not querying the database, then I would guess that it has already cached an empty array.
Therefore, the inital "if (tranItems == null)" check returns false, but it then returns an empty array as the results.

I found the issue to be that some fields retrieved by the reader were null, and you can't create a null string. The solution was essentially to use something like this for every item property:
item.Amount = (reader["Amount"] != DBNull.value) ? (decimal)reader["Amount"] : 0;

Related

How to setup gridview in mvc

I would like to setup gridview under MyTickets tab.
How can I set this view to have only tickets from username eg 'testuser' ?
In controller I have below code. Table Zgloszenia is my table where I storing all information about tickets (date,username, id etc)
public ActionResult MyTickets(Zgloszenia model)
{
if (Session["UserID"] != null)
{
test dg = new test();
var item = dg.Zgloszenia.Where(x => x.UsrUsera == model.UsrUsera).SingleOrDefault();
return View(item);
}
else
{
return RedirectToAction("Login");
}
}
In view I have this code:
#model IEnumerable<Webform.Models.Zgloszenia>
#{
ViewBag.Title = "MyTickets";
WebGrid grid = new WebGrid(Model);
}
<h2>MyTickets</h2>
#if (Session["UserID"] != null)
{
<div>
Welcome: #Session["Username"]<br />
</div>
}
#grid.GetHtml(columns: new[] {
grid.Column("Opis"),
grid.Column("Priorytet"),
grid.Column("Srodowisko"),
grid.Column("NumerTaska"),
grid.Column("test"),
grid.Column("Date")
})
When I log in to my app and click Tab "MyTicket" I'm receiving below error:
A data source must be bound before this operation can be performed.
How I can fix this issue and set up view properly ?
In your action you are selecting a single item, not a collection to enumerate. On the contrary, the WebGrid expects a collection as a data source, so the way you declared things on the view is fine.
To check if that is indeed the issue, simply remove SingleOrDefault call in your action. If your Where call returns at least one record, you should be able to see it on the page:
test dg = new test();
var items = dg.Zgloszenia.Where(x => x.UsrUsera == model.UsrUsera).ToList();
return View(items);
Are you working with Visual studio? If yes, you have to make a dataset. (local or online) You do not have a database at the moment so he saves it nowhere.

ASP .NET MVC overriding when edit while multiple records are opened at once in different tabs-

My custom MVC web application has an overriding problem. I have custom edit page and im edditing university teachers, so they all have a profile picture. This is my edit controller which loads the view.
public ActionResult Edit(int? id)
{
if (cf.CheckDBConnection() != true)
{
return View("~/Views/Error/NoDBConnection.cshtml");
}
if (checksession.CheckSessionState() != 1)
{
return View("~/Views/Error/NotAdmin.cshtml");
}
if (id == null)
{
return View("~/Views/Error/BadRequest.cshtml");
}
var disciplina = db.tblDisciplines.ToList();
ViewBag.disciplina = disciplina;
var specialies = db.tblSpecialities.ToList();
ViewBag.specialty = specialies;
var Ischecked = db.tblTeachersDisciplines.Where(p => p.TeacherID == id).Select(p => p.DisciplineID).ToArray();
ViewBag.Checked = Ischecked;
tblTeacher tblTeacher = db.tblTeachers.Find(id);
var IscheckedSpec = db.tblTeachersSpecialities.Where(p => p.TeacherID == tblTeacher.ID).Select(p => p.SpecialityID).ToArray();
ViewBag.CheckedSpec = IscheckedSpec;
TempData["Photo"] = tblTeacher.ProfilePicture;
if (tblTeacher == null)
{
return View("~/Views/Error/NotFound.cshtml");
}
var department = tblTeacher.TDepartment;
ViewBag.department = department;
return View(tblTeacher);
}
This is the controller that runs after clicking "save"
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,FirstName,SecondName,Title,PersonalCabinet,Laboratory,TelNum,Email,VisitingHours,PersonalInterests,TDepartment,ProfilePicture")] tblTeacher tblTeacher, HttpPostedFileBase file, int[] discipline, int[] specialty, string department)
{
var disciplina = db.tblDisciplines.ToList();
ViewBag.disciplina = disciplina;
var specialies = db.tblSpecialities.ToList();
ViewBag.specialty = specialies;
var Ischecked = db.tblTeachersDisciplines.Where(p => p.TeacherID == tblTeacher.ID).Select(p => p.DisciplineID).ToArray();
ViewBag.Checked = Ischecked;
var IscheckedSpec = db.tblTeachersSpecialities.Where(p => p.TeacherID == tblTeacher.ID).Select(p => p.SpecialityID).ToArray();
ViewBag.CheckedSpec = IscheckedSpec;
tblTeacher.TDepartment = department;
if (department == null || department == "")
{
ModelState.AddModelError("", "Изберете факултет");
return View(tblTeacher);
}
if (!ModelState.IsValid)
{
return View(tblTeacher);
}
if (discipline == null)
{
ModelState.AddModelError(string.Empty, "Моля, изберете дисциплини");
return View(tblTeacher);
}
if (specialty == null)
{
ModelState.AddModelError(string.Empty, "Моля, изберете специалност");
return View(tblTeacher);
}
try
{
var _photo = TempData["Photo"];
if (file != null)
{
if (file.ContentLength > 0 && file.ContentLength < 1000000 && file.ContentType == "image/jpeg")
{
byte[] imageBytes = null;
BinaryReader reader = new BinaryReader(file.InputStream);
imageBytes = reader.ReadBytes((int)file.ContentLength);
_photo = imageBytes;
}
else
{
ModelState.AddModelError(string.Empty, "Профилната снимка е задължителна и трябва да отговаря на описаните условия!");
return View(tblTeacher);
}
}
tblTeacher.ProfilePicture = (byte[])_photo;
db.Entry(tblTeacher).State = EntityState.Modified;
db.tblTeachersDisciplines.Where(p => p.TeacherID == tblTeacher.ID).ToList().ForEach(p => db.tblTeachersDisciplines.Remove(p));
db.tblTeachersSpecialities.Where(p => p.TeacherID == tblTeacher.ID).ToList().ForEach(p => db.tblTeachersSpecialities.Remove(p));
db.SaveChanges();
for (int i = 0; i < discipline.Length; i++)
{
using (RSEntities entities = new RSEntities())
{
tblTeachersDiscipline TD = new tblTeachersDiscipline
{
TeacherID = tblTeacher.ID,
DisciplineID = discipline[i]
};
entities.tblTeachersDisciplines.Add(TD);
entities.SaveChanges();
}
}
for (int i = 0; i < specialty.Length; i++)
{
using (RSEntities entities = new RSEntities())
{
tblTeachersSpeciality TS = new tblTeachersSpeciality
{
TeacherID = tblTeacher.ID,
SpecialityID = specialty[i],
};
entities.tblTeachersSpecialities.Add(TS);
entities.SaveChanges();
}
}
return RedirectToAction("Index");
}
catch (Exception ex)
{
DateTime currentTime = DateTime.Now;
string _error = ex.ToString();
_error = currentTime + " Exception: " + _error;
SendMailOnErrorService mail = new SendMailOnErrorService();
mail.Sendemail(_error);
return View("~/Views/Error/DataBaseError.cshtml");
}
}
The problem seems to occur in this current scenario:
When i open many "Edit" tabs and i save all of them, they all change their profile picture with the one from the last tab opened.
I reckon the problem comes fromTempData["Photo"] = tblTeacher.ProfilePicture;
as it probably takes the picture from the last tab opened and then when I save a tab it applies it to the current teacher profile.
I use TempData["Photo"] = tblTeacher.ProfilePicture; because if I dont, when I edit-save a profile it erases the profilepicture from the database as it comes as null from the view and the controller on its behalf inserts null in the column from the DB.
One of the solutions may be not using TempData at all but what I want to know is why the problem occurs and how it can be solved while still using TempData
PS - I've done some testing and its 100% sure the problem is with TempData
The key string you are using for for TempData is "Photo". The value for returned from TempData for key "Photo" is whatever you last set it to in your HttpGet Edit method. If you have multiple Edit tabs open, the value for TempData["Photo"] will be whatever you last set it to for the last Edit tab open.
If you want to use TempData for each open Edit tab you would need a unique key for each Edit tab like so:
Edit tab 1: TempData["EditTab1_Photo"]
Edit tab 2: TempData["EditTab2_Photo"]
And so on.
And a unique TempData key for each Edit View request is not easy to do, you will have to generate a unique TempData key for each HTTP request and somehow know which key to use in the HttpPost Edit HTTP requests. Challenging and overly complex solution this is.
You do not have a ViewModel for your View and that is what you need, you should stop using TempData right away, see below for what TempData is used for.
The way to solve this problem is a standard ViewModel, an essential core tenant of how MVC data sharing is done between a View and a Controller. A Teacher ViewModel, which will have a byte Photo {get;set;} property among other properties that relate, mostly 1 to 1 in type and name as your tblTeacher entity,thus a unique instance of that Teacher ViewModel will be what is passed back and forth for each open Edit tab.
Currently you are sending the data Entity (tblTeacher) directly to the View which is not ever correct in a MVC design pattern. Your View should not depend on a persistence (database) Entity structure/representation, a structure which will change in the future and when that happens, it should not break your View, your View should depend on a ViewModel which translates your Entity into a structure specific to the View, the controller has the responsibility of translating/mapping the structure of the Entity and the ViewModel back and forth between HTTP requests.
A quote and article for you of what TempData is used for:
"TempData, on the other hand, is geared specifically for working with data on HTTP redirects, so remember to be cautious when using TempData."
http://rachelappel.com/when-to-use-viewbag-viewdata-or-tempdata-in-asp-net-mvc-3-applications/

Create Hyperlinks from controller in asp.net MVC

I have a controller, which creates breadcrumbs as follows:
Software > Windows 7 > Outlook 2007
The code to create this is:
ViewBag.breadcrumbs = string.Join(" > ", cbh.Select(i => i.Title));
Is there a straightforward way of making the breadcrumbs hyperlinks, which would point to (i.ParentID) ie:
Software -> forum/index/12
Windows 7 -> forum/index/19
Outlook 2007 -> forum/index/23
Or should I just loop through cbh and manually build <a href=...> strings, and pass those to the view?
Thank you,
Mark
Your best bet is to put the required items into the model then loop through them.
Try something like this:
Model
public class Model
{
public struct BreadCrumb
{
public string Title;
public string Url;
}
public List<BreadCrumb> Breadcrumbs { get; set; }
}
View
#{ int index = 0; }
#foreach(var crumb in this.Model.Breadcrumbs)
{
#(crumb.Title)
if(index < this.Model.Breadcrumbs.Count - 1)
{
<span>></span>
}
index++;
}
Yes, you should build your breadcrumb links in the view. If it helps, you can create a BreadCrumbModel class (if you don't already have one).
ViewBag.breadcrumbs = cbh.Select(i => new BreadCrumbModel()
{
Id = i.Id,
Title = i.Title
});
#{
var printSeparator = false;
}
#foreach(BreadCrumbModel bc in ViewBag.breadcrumbs)
{
#if(printSeparator)
{
<span class="breadcrumb-separator"> > </span>
}
<span class="breadcrumb">
#Html.ActionLink(bc.Title, "index", "forum", new { id = bc.Id });
</span>
#{
printSeparator = true;
}
}
If you want to have breadcrumbs between different controllers and actions (not just forum / index), then add those as properties of your BreadCrumbModel.

Out of Memory at line XXXX

can anyone help me how to resolve the out of memory error on my asp page? im using linq to sql.. after adding data several data.. like more than 10 rows. in the grid. an out of memory error occurs.. attached herewith is my add function..
public ServiceDetail checkservicedetailid()
{
string ServiceName = ViewState["Tab"].ToString();
ServiceDetail checkservicedetailid = ServiceDetails_worker.get(a => a.ServiceName == ServiceName && a.MarginAnalysisID == checkmarginanalysisid().MarginAnalysisID).SingleOrDefault();
return checkservicedetailid;
}
public IEnumerable<ServiceDetail> get(Expression<Func<ServiceDetail, Boolean>> express)
{
return ServiceDetailsDB.ServiceDetails.Where(express);
}
protected void btnSaveEmptyOC_Click(object sender, EventArgs e)
{
try
{
if (checkservicedetailid() != null)
{
CashExpense tblCashExpenses = new CashExpense();
Guid CashExpensesID = Guid.NewGuid();
tblCashExpenses.CashExpensesID = CashExpensesID;
tblCashExpenses.ServiceDetailsID = checkservicedetailid().ServiceDetailsID;
tblCashExpenses.Description = txtDescriptionEmptyOC.Text;
tblCashExpenses.Quantity = Decimal.Parse(txtQTYEmptyOC.Text);
tblCashExpenses.UnitCost = Decimal.Parse(txtUnitCostEmptyOC.Text);
tblCashExpenses.CreatedBy = User.Identity.Name;
tblCashExpenses.DateCreated = DateTime.Now;
tblCashExpenses.CashExpensesTypeID = "OTHER";
CashExpenses_worker.insert(tblCashExpenses);
CashExpenses_worker.submit();
//Clear items after saving
txtDescriptionEmptyOC.Text = "";
txtQTYEmptyOC.Text = "";
txtUnitCostEmptyOC.Text = "";
ValidationMessage.ShowValidationMessage(MessageCenter.CashExpenseMaintenace.InsertOC2, "SaveEmptyOC", this.Page);
MyAuditProvider.Insert(this.GetType().ToString(), ViewState["MarginAnalysisID"].ToString(), MessageCenter.Mode.ADD, MessageCenter.CashExpenseMaintenace.InsertOC2, Page.Request, User);
divOtherCost.Visible = false;
grd_othercost.Visible = true;
btnaddothercost.Visible = true;
}
else
{
//Displays a Message on the Validation Summary (Service Id does not exist)
ValidationMessage.ShowValidationMessage(MessageCenter.CashExpenseMaintenace.SaveServiceDetailOC, "SaveEmptyOC", this.Page);
}
}
catch
{
//Displays a Message on the Validation Summary (Error on Saving)
ValidationMessage.ShowValidationMessage(MessageCenter.CashExpenseMaintenace.InsertOCError, "SaveEmptyOC", this.Page);
}
finally
{
//Rebinds the Grid
populategrd_othercost();
}
}
I'm guessing from your code here:
ServiceDetail checkservicedetailid = ServiceDetails_worker.get(
a => a.ServiceName == ServiceName &&
a.MarginAnalysisID == checkmarginanalysisid().MarginAnalysisID
).SingleOrDefault();
that .get() is taking a Func<SomeType, bool>, and you are doing something like:
var row = dbCtx.SomeTable.Where(predicate);
(please correct me here if I'm incorrect)
This, however, is using LINQ-to-Objects, meaning: it is loading every row from the table to the client and testing locally. That'll hurt memory, especially if a different db-context is created for each row. Additionally, the checkmarginanalysisid() call is being executed per row, when presumably it doesn't change between rows.
You should be testing this with an Expression<Func<SomeType, bool>> which would be translated to TSQL and executed at the server. You may also need to remove untranslatable methods, i.e.
var marginAnalysisId = checkmarginanalysisid().MarginAnalysisID;
ServiceDetail checkservicedetailid = ServiceDetails_worker.get(
a => a.ServiceName == ServiceName &&
a.MarginAnalysisID == marginAnalysisId
).SingleOrDefault();
where that is get(Expression<Func<SomeType, bool>>).
I tried all of the solution given to me both by my peers as well as the solution provided here, from GC.Collect, to disposing linq datacontext after use etc. however the error keeps on occurring, i then tried to remove the update panel, Ive read a site that showed how ridiculous update panel when it comes to handling data esp when a function is done repeatedly. And poof! the memory problem is gone!

Retrieve all items from a SharePoint Field Choice Column

I am playing around with a SharePoint server and I am trying to programmatically add a service request to microsoft's call center application template. So far, I have had pretty good success. I can add a call for a specified customer and assign a specific support tech:
private enum FieldNames
{
[EnumExtension.Value("Service Request")]
ServiceRequest,
[EnumExtension.Value("Customer")]
Customer,
[EnumExtension.Value("Service Representative")]
ServiceRepresentative,
[EnumExtension.Value("Assigned To")]
AssignedTo,
[EnumExtension.Value("Software")]
Software,
[EnumExtension.Value("Category")]
Category
}
private void CreateServiceCall(string serviceCallTitle, string customerName, string serviceRep)
{
SPSite allSites = new SPSite(siteURL);
SPWeb site = allSites.AllWebs[siteName];
SPListItemCollection requestsList = site.Lists[serviceRequests].Items;
SPListItem item = requestsList.Add();
SPFieldLookup customerLookup = item.Fields[FieldNames.Customer.Value()] as SPFieldLookup;
item[FieldNames.ServiceRequest.Value()] = serviceCallTitle;
if (customerLookup != null)
{
using (SPWeb lookupWeb = allSites.OpenWeb(customerLookup.LookupWebId))
{
SPList lookupList = lookupWeb.Lists.GetList(new Guid(customerLookup.LookupList), false);
foreach (SPListItem listItem in lookupList.Items)
{
if (listItem[customerLookup.LookupField].ToString() != customerName) continue;
item[FieldNames.Customer.Value()] = new SPFieldLookupValue(listItem.ID, customerName);
break;
}
}
}
SPUserCollection userCollection = site.SiteUsers;
if (userCollection != null)
{
foreach (SPUser user in userCollection)
{
if (user.Name != serviceRep) continue;
item[FieldNames.AssignedTo.Value()] = user;
break;
}
}
item.Update();
site.Close();
allSites.Close();
}
I added two custom columns (category, software) to the default list:
I populated both of these columns inside of SharePoint, now I want to retrieve that data so I can use it in the code snippet I posted to assign the proper category/software etc to the call. I have not been able to get the list in the code, I have tried using a item["Software"], site.Lists["Software"] and a couple of others, but so far all I have come up is null.
Can anyone point me in the right direction for this? Thanks!
SPFieldMultiChoice and related fields have a Choices property:
SPFieldMultiChoice software = item.Fields[FieldNames.Software.Value()] as SPFieldMultiChoice;
StringCollection softwareChoices = software.Choices;
If you need to set a value on the field, use the SPFieldMultiChoiceValue type:
SPFieldMultiChoiceValue values = new SPFieldMultiChoiceValue();
values.Add("Choice 1");
values.Add("Choice 2");
item[FieldNames.Software.Value()] = values;

Categories

Resources