WCF error when querying using LINQ to Objects - c#

Please excuse me if this is a duplicate question - I have searched but have not found anything that explains my problem.
I created an ASP.NET website. I have 3 layers - Data Access, Business, Presentation. I used shared entities throughout using Entity Framework.
Presentation: UI
Business: WCF Service
Data Access: code to connect to DB (contains the EF context, connection string)
Entities: .edmx - EF Entities. These are shared throughout all 3 layers.
I have come upon some odd behavior in the WCF service which I cannot understand. My original code had a function in the DAL which queried the db for all customers, and returned a List. The service than further queried this list based on what the UI requested and returned a List to the Presentation Layer.
DAL:
public List<Customer> GetCustomers()
{
List<Customer> custList= new List<Customer>();
try
{
using (NorthWindsEntities context = new NorthWindsEntities(connectionString))
{
custList= context.Customers
.Include("Orders")
.Include("OrderDetails")
.ToList();
}
return custList;
}
catch (Exception ex)
{
//handle exception
return custList;
}
}
WCF:
public List<Customer> GetCustomersByState(string state)
{
contextManager contextDAL = new contextManager();
List<Customer> custList = contextDAL.GetCustomers()
.Where(c => c.State == state)
.ToList();
return custList;
}
When I debugged the code, eveything worked fine, but when the service tried to return the custList to the client, I got this error: An error occurred while receiving the HTTP response to localhost/WCF/MyService. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.
I changed my DAL to inclued the entire query and my WCF Service to this:
public List<customer> GetCustomersByState(string state)
{
contextManager contextDAL = new contextManager();
List<Customer> custList = contextDAL.GetCustomers(state)
return custList;
}
This worked fine. I also tried to put the entire query in the service (the service connected directly to the context and got the list) and that worked fine as well.
So basically, my question is, why can't the WCF service return a list of objetcs that were queried using Linq to Objects (the DAL returns a List of customer so all further querying is linq to objects). It works fine using Linq to Entities (with all the code in the service) and it works fine with no querying at all - just getting a list and returning it.
I'm sorry for the lengthy post, I tried to include all necessary details...Thanks!

After much trial and error, I have found that it is not a querying issue, it is a size issue. Though the WCF service was filtering my data to only return a few records, I guess it retained some sort of reference to the rest of the data because it errored with too much data. If I do a partial filter on the DAL and then continue to filter on WCF layer, coming out the same amount of records as I originally tried to return, it returns the list without a problem.
I cannot explain why this happens, I don't know much about WCF, just explaining what I did to get around it in case anyone else has this problem.

Related

VS 17 Loop Through Row, Call API, Injest JSON

Goal: I have a list of IDs in a MSSQL DB that I want to send to an API (NPI Registry API) then parse the returned JSON to
ingest certain elements back into the DB.
Ask: I am comfortable in t-sql inside of SSMS but very much a beginner anywhere outside, I'm hoping this exercise will be a good foray into using other web interfaces. I don't want anyone to explain exactly how to do this but I was hoping someone could outline the most efficient route so I can tackle the rest myself. I am hoping to do this through VS17 but am willing to go outside of VS if that's a better solution.
These are the steps you should generally follow:
Read the Rows
Here you can use ADO.NET simple queries or any framework like Entity Framework or Dapper.NET
Make your API calls
Here you can use something like this.
It may vary a little bit depending on how you will use the IDs retrieved from the DB:
:
var companies = db.Companies.ToList(); //supposing db is you DbContext on EF
var myTypesFromApi = new List<MyTypeT>();
companies.ForEach(async company =>
{
try
{
using (var client = new HttpClient())
{
var res = await client.GetStringAsync("http://myapi.com/v1/");
myTypesFromApi.Add(JsonConvert.DeserializeObject<MyTypeT>(res));
}
}
catch (Exception e)
{
//catch properly your exception here
}
});
Send it back to the DB

BreezeJs + EF + Angular capabilities with DTO's

i'm evaluating Breeze.Js for a large enterprise, data oriented, Angular 5 application in order to take advantage of the following features that are missing in the vanilla Angular framework:
client side data store
client side model state tracking
client side model validation rules
bulk data persistence (SaveChanges() method to persist all entities).
For test purposes i've written the following simple BreezeController in my ASP.NET WebApi + EntityFramework server side:
[EnableCors(origins: "*", headers: "*", methods: "*")]
[BreezeController]
public class PeopleController : ApiController
{
private AdventureWorksDbContext db = new AdventureWorksDbContext();
#region "Breeze"
readonly EFContextProvider<AdventureWorksDbContext> _contextProvider =
new EFContextProvider<AdventureWorksDbContext>();
// ~/breeze/todos/Metadata
[HttpGet]
public string Metadata()
{
return System.Text.Encoding.UTF8.GetString(AdventureWorks.WebApi.Properties.Resources.WebApiMetadata);
}
// ~/breeze/todos/Todos
// ~/breeze/todos/Todos?$filter=IsArchived eq false&$orderby=CreatedAt
[HttpGet]
public IQueryable<PersonDTO> GetPeople()
{
return db.People.ProjectTo<PersonDTO>();
}
// ~/breeze/todos/SaveChanges
[HttpPost]
public SaveResult SaveChanges(Newtonsoft.Json.Linq.JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
#endregion
}
As you can see in my example (it uses AdventureWorks DB) i've done the following modifications:
1) "GetPeople()" endpoint returns a queryable of DTO ("ProjectTo" extension is provided by Automapper). I need to do this in order to shape the model in a usable way for the client, avoid recursions, deep dive in the schema, big fields serialization and so on.
2) "Metadata()" endpoint returns a string resource that represents metadata of the DTO class. I builded it using "PocoMetadata" tool of the "Breeze Tooling Suite" (https://github.com/Breeze/breeze.tooling). This is needed because i can't return the _contextProvider.Metadata() result as long as i'm using DTO's and not EF POCO class.
Now, if in my Angular 5 client i issue an ODATA query like the following i can see that executeQuery() method actually works:
export class BreezeDataStoreComponent implements OnInit {
private _em: EntityManager;
constructor() {
this._em = new EntityManager({
serviceName: 'http://localhost:31328/breeze/People'
});
}
ngOnInit() {
const query = EntityQuery.from('GetPeople')
.where('FirstName', FilterQueryOp.StartsWith, 'F')
.orderBy('LastName', true);
this._em.executeQuery(query).then(res => {
// Here i can get all People instances.
// Now i try to get the first, edit the name and saveChanges.
(res.results[0] as any).FirstName = 'Franklino';
this._em.saveChanges().then(saveResult => {
const test = saveResult.entities;
});
});
}
}
Unfortunately problems comes with SaveChanges().
When the Angular client calls that method, in my server side i get the following error:
System.InvalidOperationException: Sequence contains no matching
element
I think it's due to the fact that i'm calling SaveChanges() over an EF context provider passing a JObject bundle referred to DTO instead of POCO class.
So my question is:
Is it possible to use BreezeJs query and bulk persistence (SaveChanges() method) using DTO's? It's a pretty common need in big data-centric enterprise applications since i think it's a bad practice exposing EF POCOs on WebApi.
should i rely instead over a classic WebApi that respond to the POST\PUT\DELETE HTTP verbs? In that case, how to configure Breeze client in order to contact those endpoints instead of "SaveChanges" when persisting data?
If Breeze is not suitable for this needs are there other technolgies that provides the 4 abovementioned points?
Thank you very much.
To make SaveChanges work with your DTOs, you would need to either
Write your own method to unpack the JObject saveBundle, or
Use the BeforeSaveChanges method to modify the dictionary of DTOs and replace them with entities that EF understands.
Number 2 seems like the better choice. If you do not have a 1:1 match between entities and DTOs, some logic would be required when doing the mapping.

New DbContext is not pulling updated data if database is modified manually

Background
I have a central database my MVC EF web app interacts with following best practices. Here is the offending code:
// GET: HomePage
public ActionResult Index()
{
using (var db = new MyDbContext())
{
return View(new CustomViewModel()
{
ListOfStuff = db.TableOfStuff
.Where(x => x.Approved)
.OrderBy(x => x.Title)
.ToList()
});
}
}
I also modify the data in this database's table manually completely outside the web app.
I am not keeping an instance of the DbContext around any longer than is necessary to get the data I need. A new one is constructed per-request.
Problem
The problem I am having is if I delete a row or modify any data from this table manually outside the web app, the data being served by the above code does not reflect these changes.
The only way to get these manual edits of the data to be picked up by the above code is to either restart the web app, or use the web app to make a modification to the database that calls SaveChanges.
Log Results
After logging the query being executed and doing some manual tests there is nothing wrong with the query being generated that would make it return bad data.
However, in logging I saw a confusing line in the query completion times. The first query on app start-up:
-- Completed in 86 ms with result: CachingReader
Then any subsequent queries had the following completion time:
-- Completed in 0 ms with result: CachingReader
What is this CachingReader and how do I disable this?
Culprit
I discovered the error was introduced elsewhere in my web app as something that replaced the underlying DbProviderServices to provide caching, more specifically I am using MVCForum which uses EF Cache.
This forum's CachingConfiguration uses the default CachingPolicy which caches everything unless otherwise interacted with through the EF which was the exact behavior I was observing. More Info
Solution
I provided my own custom CachingPolicy that does not allow caching on entities where this behavior is undesirable.
public class CustomCachingPolicy : CachingPolicy
{
protected override bool CanBeCached(ReadOnlyCollection<EntitySetBase> affectedEntitySets, string sql, IEnumerable<KeyValuePair<string, object>> parameters)
{
foreach (var entitySet in affectedEntitySets)
{
var table = entitySet.Name.ToLower();
if (table.StartsWith("si_") ||
table.StartsWith("dft_") ||
table.StartsWith("tt_"))
return false;
}
return base.CanBeCached(affectedEntitySets, sql, parameters);
}
}
With this in place, the database logging now always shows:
-- Completed in 86 ms with result: SqlDataReader
Thanks everyone!

WCF Underlying Connection Error while returning DataTable

I created the webservices method that returns a dataTable.
However, I am getting the error:
The underlying connection was closed: The connection was closed unexpectedly
This is my method in the IWS file
[ServiceContract]
public interface IWsBabyCare
{
[OperationContract]
DataTable GetPurchaseOrderDetails();
[OperationContract]
DataTable GetPurchaseOrders(int PODID);
}
And this is my method in the WS file
public DataTable GetPurchaseOrderDetails()
{
DataTable POD = new DataTable("POD");
PurchaseOrderBLL prodBLL = new PurchaseOrderBLL();
POD = prodBLL.GetPOD();
POD.TableName = "POD";
return POD;
}
public DataTable GetPurchaseOrders(int PODID)
{
PurchaseOrderBLL prodBLL = new PurchaseOrderBLL();
return prodBLL.GetPurchaseOrders(PODID);
}
I read online that adding a name to the datatable will help but I'll just be returning an empty dataTable.
The exception you are getting can have about 500 different causes.
All we can really tell at the moment is that you have a service interface defining some operations and that your have implemented the operations.
We know nothing about the way the service is being hosted (console, IIS?), or the WCF configuration specified on the service and client. You need to supply this information in your question.
Additionally your service operations specify a return type of DataTable which is a big web service no-no. You should be passing types which define exactly the data you need (for example, if your database table contain apples, you should pass a return type of List<Apple>.
This is how I suggest you should proceed.
Create a console-based service and client project.
Create a single service operation which returns some primitive type like string.
Make all code as simple as possible. You should be able to get this working with less than 10 lines of code and about 10 lines of xml config on each of the service and client.
Once you have managed to get this working then gradually add new operations and more complex return types.

Do I need to call my domain service's context.SaveChanges after adding a new entity?

I am building an application based on Ria Services.
Amongst the default methods that were built by the Visual Studio Wizard, I can see the ones that should insert and persist new data.
For example:
public void InsertCustomer(Customer customer)
{
if ((customer.EntityState != EntityState.Detached))
{
this.ObjectContext.ObjectStateManager.ChangeObjectState(customer, EntityState.Added);
}
else
{
this.ObjectContext.Customers.AddObject(customer);
}
}
However, I don't see any this.ObjectContext.SaveChanges(), which I read should be invoked in order to insert the entity in the data store. ...do I need to modify these functions in order to persist the information?
Thanks in advance,
Cheers,
Gianluca.
When you call SubmitChanges on the client, it calls into DomainService.Submit on the server. The default implementation then sequentially calls into a number of protected virtual methods including AuthorizeChangeSet, ValidateChangeSet, ExecuteChangeSet, and PersistChangeSet. Your CUD operation will be invoked from ExecuteChangeSet and ObjectContext.SaveChanges() will be invoked from PersistChangeSet.
You don't need to modify the default methods to persist information as that will be taken care of by default. However, the design gives you the option of overriding chunks of the submit pipeline if you find more complex scenarios necessitate it.
What you should do is something like this:
//Create an instance of your Domain context in your class.
YourDomainContext context = new YourDomainContext();
if (context.HasChanges)
{
context.SubmitChanges(so =>
{
string errorMsg = (so.HasError) → "failed" : "succeeded";
MessageBox.Show("Update " + errorMsg);
}, null);
}
else
{
MessageBox.Show("You have not made any changes!");
}
Please take a look a this at this article: Using WCF RIA Services
Or take a look at this video: Silverlight Firestarter 2010 Session 3 - Building Feature Rich Business Apps Today with RIA Services

Categories