WCF web service: response is 200/ok, but response body is empty - c#

I am creating a WCF web api service. My problem is that some methods return a 200/OK response, but the headers and the body are empty.
In setting up my web service, I created an ADO.NET Entity Data Model. I chose ADO.NET DbContext Generator when I added a code generation item. In the Model.tt document, I changed HashSet and ICollection to List. I built my website.
It used to be that when I coded a method to return a List of an entity (like List<Customer> or List<Employee> in the Northwind database), it worked fine. Over time, I could not return a List of any of those, and could only grab one entity. Now, it's gotten to a point where I can return a List<string> or List<int>, but not a List or an instance of any entity. When I try to get a List<AnyEntity>, the response is 200/OK, but the response headers and body are empty.
I have tried using the debugger and Firefox's Web Console. Using FF's WC, I could only get an "undefined" status code. I am not sure where to go from here.
EDIT: In trying to grab all Areas from the database, I do this:
[WebGet(UriTemplate = "areas")]
public List<a1Areas> AllAreas()
{
return context.a1Areas.ToList();
}
I would appreciate any more methods for debugging this. Thanks in advance.
Found the answer, thanks to Merlyn!
In my Global.asax file, I forgot to comment out two lines that took care of proxies and disposing of my context object. The code is below:
void Application_BeginRequest(object sender, EventArgs e)
{
var context = new AssignmentEntities();
context.Configuration.ProxyCreationEnabled = false;
HttpContext.Current.Items["_context"] = context;
}
void Application_EndRequest(object sender, EventArgs e)
{
var context = HttpContext.Current.Items["_context"] as AssignmentEntities;
if (context != null)
{
context.Dispose();
}
}

Related

Conditional logic in MS Load Test Project

Ok, I'm stuck. I've got a Web API that I benchmark using VS integrated Load Test project. There's one catch though, I need conditional logic. For example:
if Svc1 returns param1, then next requests should go to Svc2.
I'm using ExtractionRule to extract the parameter from web API call, and if present, add that in Context and LoadTestUserContext. Here's the code:
public override void Extract(object sender, ExtractionEventArgs e)
{
var serializer = new JsonNetSerializer();
var str = e.Response.BodyString;
var result = serializer.Deserialize<SpinResult>(str);
if (result.BonusRemainingTrials > 0)
{
e.WebTest.Context.Add("bonus", result.BonusRemainingTrials);
var userContext = (LoadTestUserContext)e.WebTest.Context["$LoadTestUserContext"];
userContext["bonus"] = result.BonusRemainingTrials;
e.Success = true;
}
}
and here's test conditional logic:
Everything works fine, until server returns the parameter, after which load test should invoke the second service (Svc2), which doesn't happen. No matter if "bonus" parameter assigned or not, the test always invokes Svc1. Any help why it's so stubborn?
Many thanks.

ASP.NET OData v3 vs Excel 2013: ignoring pagination?

I'm trying to integrate an OData Web Api with Excel 2013 but I'm facing some troubles
Scenario:
Large database view with ~40 millions rows (MySQL)
Database-first EDMX on top of it (Entity Framework 6.1.0)
EntitySetController.Get() to supply data to the clients (WebApi 2.1)
Excel 2013 to consume OData feed
My server side code had to take into account the huge data that is behind, so I decorated my Get() with [Queryable(PageSize=50)]
public class SerieValuesController : EntitySetController<SerieValue, int>
{
#region DB Property
private EDMWarehouseViewsContainer _DB = null;
public EDMWarehouseViewsContainer DB
{
get
{
if (_DB == null)
_DB = new EDMWarehouseViewsContainer();
return _DB;
}
}
#endregion
[Queryable(PageSize=50)]
public override IQueryable<SerieValue> Get()
{
var options = this.QueryOptions;
IQueryable results = DB.SerieValues;
if (options.Filter != null)
{
results = options.Filter.ApplyTo(results, new ODataQuerySettings());
}
if (options.Top != null)
{
results = options.Top.ApplyTo(results, new ODataQuerySettings());
}
return results as IQueryable<SerieValue>;
}
protected override SerieValue GetEntityByKey(int id)
{
SerieValue entity = DB.SerieValues.Find(id);
return entity;
}
}
If I try to get data in a browser, with these URLs
http://mymachine.lan/odata/SerieValues (this gets correctly the first 50 rows in the view, along with a oData.nextLink node)
http://mymachine.lan/odata/SerieValues?$skip=50 (this get correctly the next 50 rows)
When I try to consume this feed from Excel 2013, it starts downloading the data automatically, page by page, until the memory blows.
So I changed my [Queryable] decoration as this
[Queryable(PageSize=50, MaxSkip=5000)]
After checking in the browser ($skip=4999 works, $skip=5000 works, $skip=5001 gives an error), I tried to download the data in excel again.
Unfortunately Excel stops at 5000 giving a Server Error and no data is displayed.
How can I make it work?
Yes, Excel will ignore paging when achieving data from OData service.
Since you set MaxSkip=5000, it will fail when the excel tries to get the 5001th record. So what is your expected behavior? If you wanna control the amount of return, you can combine $skip and $top in your query, and set MaxSkip and MaxTop in service to control the maximum value.

Implementing Audit Log / Change History with MVC & Entity Framework

I am building in a Change History / Audit Log to my MVC app which is using the Entity Framework.
So specifically in the edit method public ActionResult Edit(ViewModel vm), we find the object we are trying to update, and then use TryUpdateModel(object) to transpose the values from the form on to the object that we are trying to update.
I want to log a change when any field of that object changes. So basically what I need is a copy of the object before it is edited and then compare it after the TryUpdateModel(object) has done its work. i.e.
[HttpPost]
public ActionResult Edit(ViewModel vm)
{
//Need to take the copy here
var object = EntityFramework.Object.Single(x=>x.ID = vm.ID);
if (ModelState.IsValid)
{
//Form the un edited view model
var uneditedVM = BuildViewModel(vm.ID); //this line seems to confuse the EntityFramework (BuildViewModel() is used to build the model when originally displaying the form)
//Compare with old view model
WriteChanges(uneditedVM, vm);
...
TryUpdateModel(object);
}
...
}
But the problem is when the code retrieves the "unedited vm", this is causing some unexpected changes in the EntityFramework - so that TryUpdateModel(object); throws an UpdateException.
So the question is - in this situation - how do I create a copy of the object outside of EntityFramework to compare for change/audit history, so that it does not affect or change the
EntityFramework at all
edit: Do not want to use triggers. Need to log the username who did it.
edit1: Using EFv4, not too sure how to go about overriding SaveChanges() but it may be an option
This route seems to be going nowhere, for such a simple requirement! I finally got it to override properly, but now I get an exception with that code:
public partial class Entities
{
public override int SaveChanges(SaveOptions options)
{
DetectChanges();
var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in modifiedEntities)
{
var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry).GetModifiedProperties(); //This line throws exception The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'System.Data.Objects.EntityEntry'.
var currentValues = ObjectStateManager.GetObjectStateEntry(entry).CurrentValues;
foreach (var propName in modifiedProps)
{
var newValue = currentValues[propName];
//log changes
}
}
//return base.SaveChanges();
return base.SaveChanges(options);
}
}
IF you are using EF 4 you can subscribe to the SavingChanges event.
Since Entities is a partial class you can add additional functionality in a separate file. So create a new file named Entities and there implement the partial method OnContextCreated to hook up the event
public partial class Entities
{
partial void OnContextCreated()
{
SavingChanges += OnSavingChanges;
}
void OnSavingChanges(object sender, EventArgs e)
{
var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in modifiedEntities)
{
var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties();
var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues;
foreach (var propName in modifiedProps)
{
var newValue = currentValues[propName];
//log changes
}
}
}
}
If you are using EF 4.1 you can go through this article to extract changes
See FrameLog, an Entity Framework logging library that I wrote for this purpose. It is open-source, including for commercial use.
I know that you would rather just see a code snippet showing how to do this, but to properly handle all the cases for logging, including relationship changes and many-to-many changes, the code gets quite large. Hopefully the library will be a good fit for your needs, but if not you can freely adapt the code.
FrameLog can log changes to all scalar and navigation properties, and also allows you to specify a subset that you are interested in logging.
There is an article with high rating here at the codeproject: Implementing Audit Trail using Entity Framework . It seems to do what you want. I have started to use this solution in a project. I first wrote triggers in T-SQL in the database but it was too hard to maintain them with changes in the object model happening all the time.

ODATA Consume Service Operation from C# ASP.NET 4.0

I am connecting to an ODATA Service via a C# ASP.NET application, which has service operations such as:
GetItems(int? itemID, double? price)
I can consume this without issues in my browser, e.g.
http://api.mycompany.com/companycatalogue/GetItems?itemID=4
I understand how to use LINQ to Entities to consume an ODATA service, but can't find a decent explanation of how to consume service operations like the one above in C#. I have made a web reference to the service in my Visual Studio solution.
So far, I have something like this for my usual consuming of the data:
using CompanyCatalogue; //my web reference
...
protected void Page_Load(object sender, EventArgs e)
{
CompanyCatalogueEntities dataContext = new CompanyCatalogueEntities (new Uri("http://api.mycompany.com/companycatalogue/"));
var result = from i in dataContext.Items select i; //just an example
//this is where I get into problems
var operationResults = CompanyCatalogue.GetItems(6, 20.5); //I just made this up
}
Any pointers?
OK, got the answer:
using CompanyCatalogue; //my web reference
...
protected void Page_Load(object sender, EventArgs e)
{
CompanyCatalogueEntities dataContext = new CompanyCatalogueEntities();
DataServiceQuery<GetItemsResult> q = dataContext.CreateQuery<GetItemsResult>("GetItems")
.AddQueryOption("paramName", 6)
.AddQueryOption("paramName2", 20.5);
List<GetItemsResult> items = q.Execute().ToList();
}
This may be help for you.
This sample code used to consume the service operation in the WFC Data Service. This service operation(GetRowCount) returns the integer(int) type value. input para name is "code"
var q = context.CreateQuery<int>("GetRowCount").AddQueryOption("code", "'" + serviceProvider.Code + "'");
int RecordCount = context.Execute<int>(new Uri(q.RequestUri.ToString().Replace("GetRowCount()", "GetRowCount"))).FirstOrDefault();
Have you tried using HttpWebRequest?

Silverlight 2 - Adding database records using WCF

I'm creating a simple Silverlight 2 application - a guestbook. I'm using MSSQL as the data source, I've managed to load the data but I can't find out how to add new rows (messages) to the database.
I crawled all the internet but didn't find any working solution. The SCMEssages table has four columns - MessageID, MessageDate, MessageAuthor and MessageText. Right now I have the following code in Service1 class (which implements IService1 interface) (not working though):
public void SaveMessage(SCMessage message)
{
DataClasses1DataContext db=new DataClasses1DataContext();
db.GetTable<SCMessage>().Attach(message);
db.SubmitChanges();
}
In the main class I'm simply calling this method:
private void SendBtn_Click(object sender, RoutedEventArgs e)
{
SCMessage sm = new SCMessage
{
MessageAuthor = NameTB.Text,
MessageDate = DateTime.Now,
MessageText = TextTB.Text
};
newMessages.Add(sm);
ServiceReference1.Service1Client client = new Service1Client();
client.SaveMessageAsync(sm);
}
Could anybody help me? Thanks for any suggestions!
I'm not sure I complete understand the context (like do you control your WCF service and/or your DB). But did you consider ADO.Net Data services? (also known as astoria)
(http://msdn.microsoft.com/en-us/library/cc668792.aspx)
Then you don't need to create a webservice for it, it is already created for you.
Basically it is an easy way to access your data from within Silverlight and even be able to do queries from within silverlight.
There is already a bit of doc in blogs, for example:
A quickstart is here: http://michaelsync.net/2008/01/15/consuming-adonet-data-service-astoria-from-silverlight
How to update data can be seen here: http://michaelsync.net/2008/02/10/crud-operations-in-siverlight-using-adonet-data-service
A complete working example is here: http://www.silverlightdata.com/
Note that in a lot of examples on the web the silverlight proxy is generated using the command line, that is however not needed anymore, you can do it directly from within VS using "add service reference" to your project and pointing it to your ado.net data service
Hope this helps a bit?
Tjipke
Is SCMessage decorated with the [DataContract] attribute or is it [Serializable]? Please provide us with the definition of SCMessage.
SCMessage is name of a Data Class - I created a file from template "Linq to SQL Classes" (.dbml) and drag&dropped the SCMessages table to the Designer. It's decorated with the [DataContract] attribute and I set it's DataContext's Serialiation Mode property to Unidirectional. So the content of the SCMessage class is auto-generated, but here is as least a part of it:
[Table(Name="dbo.SCMessages")]
[DataContract()]
public partial class SCMessage : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _MessageID;
private string _MessageAuthor;
private string _MessageText;
private System.DateTime _MessageDate;
#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnMessageIDChanging(int value);
partial void OnMessageIDChanged();
partial void OnMessageAuthorChanging(string value);
partial void OnMessageAuthorChanged();
partial void OnMessageTextChanging(string value);
partial void OnMessageTextChanged();
partial void OnMessageDateChanging(System.DateTime value);
partial void OnMessageDateChanged();
#endregion
public SCMessage()
{
this.Initialize();
}
[Column(Storage="_MessageID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
[DataMember(Order=1)]
public int MessageID
{
get
{
return this._MessageID;
}
set
{
if ((this._MessageID != value))
{
this.OnMessageIDChanging(value);
this.SendPropertyChanging();
this._MessageID = value;
this.SendPropertyChanged("MessageID");
this.OnMessageIDChanged();
}
}
}
And here's a little problem with Astoria - it doesn't work for me. I followed the Michael Sync's tutorial and made a few modifications, such as using of DataServiceQuery because WebDataQuery doesn't exist in final version of Astoria, etc. I ended up with two code snippets - the first is almost identical copy of the one in Michael Sync's article and the second one is using LINQ query instead of the CreateQuery method (I think both of these approaches lead to the same end). Here are the snippets:
SilverchatDBEntities entity =
new SilverchatDBEntities(new Uri("http://localhost:65373/WebDataService1.svc", UriKind.Absolute));
entity.MergeOption = MergeOption.OverwriteChanges;
DataServiceQuery<SCMessages> messages = entity.CreateQuery<SCMessages>("SCMessages");
messages.BeginExecute(
result =>
{
var mess = messages.EndExecute(result);
foreach (var mes in mess)
{
MessagesLB.Items.Add(mes.MessageAuthor);
}
},
null);
This doesn't do anything - it doesn't throw any exception and doesn't load any SCMessages neither. The second snippet is as follows:
SilverchatDBEntities entity =
new SilverchatDBEntities(new Uri("http://localhost:65373/WebDataService1.svc", UriKind.Absolute));
var query = (DataServiceQuery<SCMessages>) from m in entity.SCMessages select m;
query.BeginExecute(new AsyncCallback(result =>
{
try
{
var mes = query.EndExecute(result);
foreach (var r in mes)
{
MessagesLB.Items.Add(string.Format("{0}; {1} - {2}",
r.MessageDate.
ToString(
"d/M hh:mm",
CultureInfo.
InvariantCulture),
r.MessageAuthor,
r.MessageText));
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}), null);
This one throws exception at the 'foreach' loop - 'Object reference not set to an instance of an object'. I have no idea what the problem could be.
Chrasty,
I don't see any obvious errors yet. So in theory it shoulld work (I currently work almost daily with these kind of queries. A few questions:
1. can you use fiddler2 to see what is going over the wire. (If you don't know what fiddler is then google :-) and if after that you are using fiddler on localhost, then please add a '.' in the url in the browser (like http:\localhost.:1234\mywebsitehostingsilverlight.aspx), -> prevents an other google search)
2. Do you have a stack trace of the second one throwing an exception.
3. Did you try putting a breakpoint in your callback (first example) to see if it is called and with what result it is called
Hope these questions help a bit in solving the problem

Categories

Resources