wcf data services saving relational data in single click - wcf

I'm using WCF data services in Windows phone 7, I want to save relational data in single click how could I do that?
it's think 2 table : Category and Products
I want to save data UI :
From Cateogry Table :-
CategoryId : (auto increment)
CategoryName : abc
From product Table :-
ProductId :-(auto increment)
CategoryId :- ? ( not sure how could I retrieve )
ProductsName : xyz
on button save click:
I want to insert above data in appropriated table , how could I do that?
I am using following code for add one table data :
try
{
context = new NorthwindEntities(NorthwindUri);
context.AddToProducts(product);
context.BeginSaveChanges(new AsyncCallback((result) =>
{
bool errorOccured = false;
// Use the Dispatcher to ensure that the
// asynchronous call returns in the correct thread.
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
context = result.AsyncState as NorthwindEntities;
try
{
// Complete the save changes operation and display the response.
DataServiceResponse response = context.EndSaveChanges(result);
foreach (ChangeOperationResponse changeResponse in response)
{
if (changeResponse.Error != null) errorOccured = true;
}
if (!errorOccured)
{
MessageBox.Show("The changes have been saved to the data service.");
}
else
{
MessageBox.Show("An error occured. One or more changes could not be saved.");
}
}
catch (Exception ex)
{
// Display the error from the response.
MessageBox.Show(string.Format("The following error occured: {0}", ex.Message));
}
});
}), context);
}
catch (Exception ex)
{
MessageBox.Show(string.Format("The changes could not be saved to the data service.\n"
+ "The following error occurred: {0}", ex.Message));
}

In OData relationships are not represented as foreign keys, instead they are represented as navigation properties. And then you manipulate them through manipulating links in the client library.
Take a look at this article: http://msdn.microsoft.com/en-us/library/dd756361(v=vs.103).aspx
You can call multiple methods which modify data and then call SaveChanges to send them all to the server.
Note though, that if the server requires referential integrity and you're for example adding two related entities at the same time, you might need to use SaveChanges(Batch) (which makes the client send everything in one request and thus allows the server to process it as a single transaction).

Related

Getting a client readable message from an Npgsql.PostgresException

I'm writing a web api using PostgreSQL and am checking database constraints as part of the validation process, but I also have a global exception filter as a fallback in case something gets by when saving. My problem is that the exception doesn't seem to have any message that I can present to the client without some processing. The added image is of the PostgresException data from a breakpoint. For example, in this case I would want something along the lines of "Asset Number x already exists" or just "Asset Number must be unique". Is this something that can be configured somewhere? The place that makes the most sense is at the constraint creation code, but I couldn't find an option to do so.
modelBuilder.Entity<AssetItem>().HasIndex(item => new { item.AssetNumber }).IsUnique();
public class DbExceptionFilter : IExceptionFilter
{
private const string UNIQUE_EXCEPTION = "23505";
public async void OnException(ExceptionContext context)
{
var exceptionType = context.Exception.InnerException.GetType().FullName;
if (exceptionType == "Npgsql.PostgresException")
{
var pgException = (PostgresException) context.Exception.InnerException;
switch(pgException.SqlState)
{
case UNIQUE_EXCEPTION:
var error = new {error = "Unique Error Here"};
await WriteJsonErrorResponse(context.HttpContext.Response, HttpStatusCode.BadRequest, error);
return;
}
}
else
{
var error = new { error = "Unexpected Server Error"};
await WriteJsonErrorResponse(context.HttpContext.Response, HttpStatusCode.InternalServerError, error);
return;
}
}
private async Task WriteJsonErrorResponse(HttpResponse response, HttpStatusCode statusCode, dynamic error)
{
response.ContentType = "application/json";
response.StatusCode = (int) statusCode;
await response.Body.WriteAsync(Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(error)));
}
}
The closest thing to a user-readable message that PostgreSQL provides is the message text exposed on PostgresException.
However, as a general rule it is not a good idea to expose database errors directly to users (including web API users): these are intended to the application directly interacting with the database (i.e. your application). These messages generally don't mean much to the users of your API, and more importantly they leak potentially sensitive information about your database schema and are therefore not secure. It's especially problematic to dump/serialize the entire exception to the user as you seem to be doing (with JsonConvert.SerializeObject).
The best practice here would be to identify legitimate database exceptions that the user may trigger, intercept these and return and appropriately-worded message of your own (e.g. "A user with that name already exists").
As a side note, to identify PostgresException, rather than getting the name of the exception and comparing to that, you can simply use C# pattern matching:
if (context.Exception.InnerException is PostgresException postgresException)
{
// ...
}

Entity Framework only allow one query

I am making a call to the SQL database via Entity Framework, this call takes about 2 mins to execute.
I want to make sure this call only occurs once. Once the call is made, I place the results in cache. I notice if multiple users are on the site, it can be more than 2 mins till the data is returned, whats the best way to approach this? Should I use a mutex? or does Entity Framework (version 4) have any functionality built in to handle this type of situation. I am using MVC 4. Thank you!
public IEnumerable<AdListing> AllActiveAds()
{
try
{
if (PullCache(Constants.CacheKeys.AllActiveAds) == null)
{
using (var db = new MyEntities())
{
db.CommandTimeout = 300;
List<AdListing> results =
(from a in
db.AdListings
.Include("AdPhotos")
.Include("tbLocation")
where !a.Deleted
select a).ToList();
PushCache(results, Constants.CacheKeys.AllActiveAds);
}
}
return (List<AdListing>) PullCache(Constants.CacheKeys.AllActiveAds);
}
catch (Exception ex)
{
HandleException(ex);
return null;
}
}

nHibernate session per request and exception management

I have a scenario similar to this:
Asp.NET MVC 4 website using nHibernate Session Per Request.
The session is injected using Ninject onto a Repository with the Get and Save methods.
There are a lot of articles talking about Session Per Request and saying that is the way to do things on a web application.
But i'm having problems implementing logic like this one:
Read Data From Database
Alter Entity information
Save to Database
Read another entity
Alter entity
Save ... but an EXCEPTION OCCURS
I want to show my view with a message to the user. But i have also to refresh the resulting web page,
so i have also to read some information from the database.
According to nHibernate documentation, the session with the exception must be discarded Documentation Here
But i can't find any articles about the best way to proceed here.
What's the best approach for this situation?. I will have to inject a new Session to my repository object?.
Thanks.
You can create a new session from the SessionFactory property of the original session. You can access the original session object by either exposing it in the repository class or injecting it into the controller. Then you can create a new repository with the new session.
I do this in some of my Actions where I expect unique key violations to occur and I have to reload lookup data in the model. Here's an example:
public ActionResult Create(MeasuresEditView model)
{
if (ModelState.IsValid)
{
using (var txn = _session.BeginTransaction())
{
try
{
var measure = new Measure { Code = model.Code };
_session.Save(measure);
txn.Commit();
return RedirectToAction("Index");
}
catch (UniqueKeyException)
{
txn.Rollback();
var msg = string.Format("A measure with the code '{0}' already exists, please enter a different code or cancel.", model.Code);
ModelState.AddModelError("Code", msg);
}
catch (Exception ex)
{
if (txn.IsActive)
{
txn.Rollback();
}
log.Error("Create", ex);
throw;
}
}
}
// have to rebuild selectlist on post in new txn in case it was rolled back
using (var session = _session.SessionFactory.OpenSession())
using (var txn = session.BeginTransaction())
{
SetProductGroupSelectList(session, model, manualId);
txn.Commit();
}
return View(model);
}

Entity framework transaction error after deleting a row and inserting a new row with same primary key

I am using ASP.NET MVC2 in Visual Studio 2008. I believe the SQL Server is 2005. I am using Entity Framework to access the database.
I've got the following table with a composite primary key based upon iRequest and sCode:
RequestbyCount
iRequest integer
sCode varchar(10)
iCount integer
iRequest is a foreign key to a list of requests.
When a request is updated, I want to clear out the existing RequestbyCounts for that request and then add in the new RequestbyCounts. More than likely, the only difference between the old rows will be the Count.
For my code, I attempt it as follows:
//delete ALL our old requests
var oldEquipList = (from eq in myDB.dbEquipmentRequestedbyCountSet
where eq.iRequestID == oldData.iRequestID
select eq).ToList();
foreach (var oldEquip in oldEquipList)
{
myDB.DeleteObject(oldEquip);
}
// myDB.SaveChanges(); <---- adding this line makes it work
//add in our new requests
foreach (var equip in newData.RequestList) //newData.RequestList is a List object
{
if (equip.iCount > 0)
{
//add in our actual request items
RequestbyCount reqEquip = new RequestbyCount();
reqEquip.sCodePrefix = equip.sCodePrefix;
reqEquip.UserRequest = newRequest;
reqEquip.iCount = equip.iCount;
myDB.AddToRequestbyCount(reqEquip);
}
}
myDB.SaveChanges(); //save our results
The issue is when I run it with the intermediate SaveChanges line uncommented, it works as desired. But my understanding is that doing this breaks the transaction apart.
If I leave the intermediate SaveChanges commented out as above, the process fails and I receive a
Violation of PRIMARY KEY constraint
'PK_RequestbyCount'. Cannot insert
duplicate key in object
'dbo.RequestbyCount'.\r\nThe statement
has been terminated.
Obviously, without doing the intermediate SaveChanges, the old rows are NOT removed as desired.
I do NOT want the results saved unless everything succeeds.
I would rather not take the following approach:
//add in our new requests
foreach (var equip in newData.RequestList)
{
if (equip.iCount > 0) && (**it isn't in the database**)
{
//add in our actual request items
RequestbyCount reqEquip = new RequestbyCount();
reqEquip.sCodePrefix = equip.sCodePrefix;
reqEquip.UserRequest = newRequest;
reqEquip.iCount = equip.iCount;
myDB.AddToRequestbyCount(reqEquip);
} else if (**it is in the database**) && (equip.iCount == 0) {
**remove from database**
} else {
**edit the value in the database**
}
}
Am I stuck doing the above code that basically makes a bunch of little calls to the database to check if an item exists?
Or is there some method that tell the framework to attempt to delete the rows I want but rollback if there is a failure inserting the new rows?
You don't appear to be using transactions at all. You need to wrap all your code in
using (TransactionScope transaction = new TransactionScope())
{
...
transaction.Complete();
}
Even better
using (TransactionScope transaction = new TransactionScope())
{
try
{
your code
transaction.Complete();
}
catch(Exception)
{
// handle error
}
}
Using the try/catch block will ensure that the transaction is not committed if an exception occurs, which is what you stated you wanted.
Lot's more on entity framework transactions at Microsoft's web site. The explanations there are quite good.

RIA Services: Server process returns multiple entities but Client shows 1 entity duplicated

I am running into an issue where RIA Services returns 3 entities from the server (I have verified while debugging on the server process, and have verified via Fiddler that the service is in face returning 3 entities.
I am using MVVM so I am calling Load on the client side using a helper function that I borrowed from a Shawn Wildermuth sample: Here's that code:
// Generic query handling
protected void PerformQuery<T>(DomainContext dc, string name, EntityQuery<T> qry, EventHandler<EntityResultsArgs<T>> evt) where T : Entity
{
dc.Load<T>(qry,(r) =>
{
if (evt != null)
{
try
{
if (r.HasError)
{
evt(this, new EntityResultsArgs<T>(r.Error));
}
else if (r.Entities.Count() > 0)
{
evt(this, new EntityResultsArgs<T>(r.Entities));
}
}
catch (Exception ex)
{
evt(this, new EntityResultsArgs<T>(ex));
}
}
}, null);
}
EntityResultsArgs is a simple class that exposes an exception property (called Error) and a Results property (containing the results if we got any).
On the server we are mapping the result using AutoMapper to our exposed Domain Classes and this particular service call returns IEnumerable.
What am I missing (or what more would help someone figure this out).
Thanks!
Yep, the problem is now confirmed. I was retrieving 3 entities back from the service all with an Id (aka the "[Key]" value) of 0.