Ok I have a confusing question (I think) in regards to request and response values.
I created a request to search for Customers within a database based on the company name. Here is the code:
//Search through customers
public void ArrangeRequest()
{
_request = new CustomerSearchRequest();
_request.Company = "NewCustomers Inc";
}
Here is customer info before it is requested and given values:
//Customer Info
_request.Customer = new CustomerInfo
{
Company = "NewCustomers Inc. ",
CustStatus = Status,
CustID = custid,
Fax = "(855) 555-6956",
Phone = "(568) 895-6954",
ProviderId = 56958,
TechContact = _techcontact,
TimeZoneInfoID = "Central Standard Time",
};
This request works and when I debug I get the message that 52 customers were found. Now, each of those customers has a unique customer ID that was created when they were. When I debug I am able to see all the information for the customers including their customer ID. My problem is I am trying to output all those values to a text file. The problem is the customer ID's are in an array with all the other information in: CustomerInfo[]. Now I am able to output each individual value in the array by saying CustomerInfo[1] or CustomerInfo[2], but I want to be able to make the search and output all the values in the array without having to call each individual value.
I want this so that if I wanted to search for another company and it has 1000 results then I won't have to call each one obviously.
edited based on OPs comments:
foreach(var customer in _response.Customers)
{
Console.WriteLine(customer.CustID);
}
Related
Using EF Core
We are trying to obtain all information of an assessment, which includes its groups and all assigned users. See the
Database Diagram
What is working in following order;
HttpPost (api/Assessment/aID/groups) of an empty group to an assessment
HttpPost (api/Group/gID/users) of users to an existing group
What we are trying to accomplish (code referenced is a different example, yet same principle);
HttpPost (api/Assessment/aID/groups) where a group already contains a list of users. When trying to accomplish this, a possible object cycle was detected which is not supported.
This piece of code is currently throwing a NullReference on Address
-------------------------------------------------------------------
Group groupToCreate = new Group { Name = dto.Name, Description = dto.Description };
foreach (var u in dto.Users)
{
groupToCreate.AddUser(new User
{
Name = u.Name,
Email = u.Email,
Address = new Address
{
Country = u.Address.Country,
City = u.Address.City,
PostalCode = u.Address.PostalCode,
Street = u.Address.Street,
HouseNr = u.Address.HouseNr,
BusNr = u.Address.BusNr
}
});
}
_groupRepository.Add(groupToCreate);
_groupRepository.SaveChanges();
return groupToCreate;
HttpGet (api/Assessment) which displays its assigned groups and linked users.
This seems to be working
------------------------
groupList = _groups.Select(g => new GroupDTO
{
Name = g.Name,
Description = g.Description,
Users = g.GroupUsers.Select(u => new UserDTO
{
Name = u.User.Name,
Email = u.User.Email,
Address = new AddressDTO
{
Country = u.User.Address.Country,
City = u.User.Address.City,
PostalCode = u.User.Address.PostalCode,
Street = u.User.Address.Street,
HouseNr = u.User.Address.HouseNr,
BusNr = u.User.Address.BusNr
}
}).ToList()
}).ToList();
References:
User
Group
Assessment
AssessmentRepo
Hard to tell with the details you're providing, but I'm guessing this is due to Having two-way navigation properties? Are you using EF here?
For example, if your User has a Navigation property allowing access to the user's Group, but a Group has a collection of User objects, then each of those users would themselves have the Group expressed within them... then when trying to express this it could easily get stuck in a cycle, e.g. a user would look like:
{
"Name":"user name",
"Group":{
"Name":"group1",
"Users":[
{
"Name":"user name",
"Group":{
"Name":"group1",
"Users":{
....
}
}
}
]
}
}
.. because a User has a Group, and the Group has a list of User objects, and each one of those has a Group... etc.
This is the sort of issue that comes from mixing your Data layer and DTO objects. Change your system so the objects returned by your REST methods are new objects designed for the requirements of the API/front-end. These objects may look very similar to your DB models (at least initially) but they should not be the same objects.
Create entirely new objects which don't have any logic or navigation properties, and exist only to pass information back to API consumers. For example, a simple class to give a list of user groups and the users in those groups may be defined as:
public class UserDto
{
public string UserName { get; set; }
public IEnumerable<string> Groups { get; set; }
}
public class UserListDto
{
public IEnumerable<UserDto> Users { get; set; }
}
And then your controller action could do something like:
var users = userService.GetAllUsers();
var result = new UserListDto {
Users = users.Select(u => new UserDto{
UserName = u.Name,
Groups = u.Groups.Select(g => g.Name)
}
};
return Ok(result);
..So the thing being serialised for the response doesn't have any complicated relationships to negotiate, and more importantly a change to how you are internally storing and working with the data won't affect the external contract of your API - API consumers can continue to see exactly the same information but how you store and compile this can change drastically.
It is tempting to think "The Data I need to return is basically the same as how I store it internally, so just re-use these classes" but that's not a great idea & will only ever give problems in the long run.
To avoid having to (re-)write a lot of code to 'convert' one object into another, I'd recommend looking into something like AutoMapper as this can make that fairly easily re-usable & allow you to put all this 'Translation' stuff into one place.
I am trying to search customers who contain these fields : email or firstName or lastName or id.
must be an OR condition between of them.
for example
var freeText = "shomeone#gmai";
var customers = SearchForCustomersWhoContainsThisData(freeText)
how can i build this query in c#?
public List<Customer> SearchForCustomersWhoContainsThisData(string search_text)
{
CustomerSearch custSearch = new CustomerSearch();
SearchStringField searchField = new SearchStringField();
searchField.#operator = SearchStringFieldOperator.contains;
searchField.operatorSpecified = true;
searchField.searchValue = search_text;
CustomerSearchBasic custBasic1 = new CustomerSearchBasic();
custBasic1.firstName = searchField;
CustomerSearchBasic custBasic2 = new CustomerSearchBasic();
custBasic2.lastName = searchField;
custSearch.basic = custBasic1;
//custSearch.basic = custBasic2; how to add this with or between
// Search for the customer entity who contains this text
SearchResult response = _crmNetSuitService.search(custSearch);
var searchResults = response.recordList.Select(t => (Customer)t).ToList();
return searchResults;
}
I expect to find customers who one of these fields contains this search-text:
email, fName, lName, Id.
As per NetSuite SuiteAnswers id 31408
Web Services > Search > How to set a field filter for one value OR another value
Currently, expressions are not supported via Web Services searches.
In order to Search where a field is one value or another. It is necessary to submit two search requests, then merge the results in the application code.
I have more than 1000 customer(s) and invoice(s) and I am trying to fetch all those customers and invoice(s) into a drop-down list.
Documentation on the QBO site suggests that we should need to use pagination if I want to load all the customers in a grid, but what I want is to load all the customer(s) and invoice(s) in a drop-down list.
I am getting the following exception when I try to fetch more than 1000 customer(s) and invoice(s):
Validation Exception was thrown.
Details: QueryValidationError: value 100000 is too large. Max allowed value is 1000.
I am trying to fetch all the customers using the following code
public static List<Customer> GetAllQBOCustomers(ServiceContext context)
{
return Helper.FindAll<Customer>(context, new Customer(),1,100000);
}
I wrote the below code and solved my issue.
1. First I get the count of all the customers
2. Then I get all the customers in chunks and the chunk size is 1000
3. Create a List for customers.
4. Define 3 integer type variables for counting.
5. After that use do-while loop
6. Add all the customers are added to the main customer list
string strQuery = "Select Count(*) From Customer";
string custCount = qboAccess.GetCutomerCount(qboInz.QboServiceContext, strQuery);
List<qboData.Customer> customers = new List<Customer>();
int maxSize = 0;
int position = 1;
int count = Convert.ToInt32(custCount);
do
{
var custList = qboAccess.GetAllQBOEntityRecords(qboInz.QboServiceContext, new Customer(), position, 1000);
customers.AddRange(custList);
maxSize += custList.Count();
position += 1000;
} while (count > maxSize);
The straightforward answer is to loop enough times to get the records you need:
public static List<Customer> GetAllQBOCustomers(ServiceContext context)
{
var list = new List<Customer>();
for (int i=0; i<=10000; i+= 1000)
{
var results = Helper.FindAll<Customer>(context, new Customer(),i, 1000);
list.AddRange(results);
}
return list;
}
Or if you want to try to do it in parallel (and the API allows concurrent connections):
public static List<Customer> GetAllQBOCustomers(ServiceContext context)
{
var bag = new ConcurrentBag<Customer>();
Parallel.ForEach( Enumerable.Range(0, 10), i =>
{
var results = Helper.FindAll<Customer>(context, new Customer(),i * 1000, 1000);
bag.AddRange(results);
});
return bag.ToList();
}
Since the series of calls is likely to be expensive, I suggest you cache the results.
You can't download those records all at once. That is what the error is telling you - very clearly. There's no magic way to avoid the server's rules.
However, I really think you should not download them all at once anyway. A drop-down list is not a good way to display that amount of data to users. Consider the user experience - would you want to scroll through a list of thousands of customers to try and find the one you want? Or would it be easier to start typing part of the name and have it pop up a short list of possible matches to choose from?
A more user-friendly way to implement this would be use an auto-complete box instead of a drop-down list, and after the user has typed a few characters, it can use AJAX to search the API for customers whose names or IDs contain those characters. Then you'll only need to return a small number of records each time, and the user will not be stuck having to scroll for 10 minutes just to find a customer at the bottom of a list of 10,000 records.
I have this query that was recently changed to allow searches using lists. However, the logic doesn't seem correct. My initial search logic was as follows:
data = data.where(u=>u.location.contains(FilterInput.RepositoryName)).ToList();
This worked for individual inputs and the logic made sense. In the data result, check if location field contains the Input variable
However in order to handle inputs that are lists, I had to change it to the bottom code which is in this Input list, check if the it contains the location field.
The database outputs data as follows:
Output = {arhde, brhje, ckio}
That means my list input is a small section of what the database contains.
FilterInput.RepositoryName = {a,b,c}
data = (from item in dbContext.Documents
join id in initialData
on item.Id equals id.DocumentId
select new DocumentsListViewModel
{
Id = item.Id,
Name = item.Name,
ApplicationName = item.ApplicationName,
ApplicationSecretKey = item.ApplicationSecretKey,
Link = item.Link,
Location = item.Location,
FileType = item.FileType,
CreatedOn = item.CreatedOn
}).ToList();
if (FilterInput.RepositoryName.Count>0)
{
data = data.Where(u => FilterInput.RepositoryName.Contains(u.Location)).ToList();
}
I don't know if its possible to change this logic to use the first one but accomodate lists as well?
Im developing a tool that needs to access to the names.nsf database inside IBM Lotus Notes, and, using the lotus contact ID (Employee ID) (this id will be provided by the user), retrieve the full information of the person (Name, Position, Phone #....)
I found an example at Codeproject.com (http://www.codeproject.com/Articles/18517/Lotus-Notes-Integration-with-Microsoft-NET-Platfor), however it takes around 10 minutes to get the information the way the example does it (the database has more or less 5000 entries), so I'm searching for a faster way of doing it (if I actually use Lotus notes for this it takes about a second!).
Is there a way to accomplish this task without having the user waiting for minutes?
Thought that maybe you can help me out with this one.
The sample you are using goes through the view using
NotesViewEntry viewEntry = notesViewCollection.GetNthEntry( rowCount );
This is (one of) the worst methods to use as it goes for every iteration from the top of the view and iterates through all docs until it reached the nth document.
There are two options:
1) Optimize this code by using
NotesViewEntry viewEntry = notesViewCollection.GetFirstEntry();
and at the end
viewEntry = notesViewCollection.GetNextEntry(viewEntry);
2) (in my humble opinion the better way): Change the code:
- you need a view with the first column sorted by your key => contact ID (Employee ID)
- You can the access the ViewEntry by a code like
LotusNotesView.GetEntryByKey( EmployeeID, true);
If you are lucky the names.nsf is full text indexed. If it's not you could try to ask if it could be full text indexed. When it's indexed you can get the person document quicly like this:
LotusNotesView.FTSearch("[EmployeeID]=1234567", 1);
NotesDocument docPerson = LotusNotesView.GetFirstDocument();
The use of GetNthEntry certainly causes some performance issues. I've taken the relevant code from that site and rewrote it to use the GetFirst/GetNext pattern, which is recommended for all view processing in Lotus Notes.
Note this hasn't been tested, of course. The point is to get the first entry in your collection, check that it is an object, and then process it. At the end of the loop, get the next entry and repeat until you hit null.
NotesViewEntryCollection notesViewCollection = LotusNotesView.AllEntries;
NotesViewEntry viewEntry = notesViewCollection.GetFirstEntry();
while (viewEntry != null)
{
//Get the first document of particular entry.
NotesDocument document = viewEntry.Document;
object documentItems = document.Items;
Array itemArray1 = (System.Array)documentItems;
for( int itemCount=0 ; itemCount< itemArray1.Length; itemCount++ )
{
NotesItem notesItem =
(Domino.NotesItem)itemArray1.GetValue( itemCount );
//compare field value with specific value entered by user
if( notesItem.Text !=null )
{
if( (notesItem.Text.ToUpper()).StartsWith( fieldValue ))
{
Contact contact = new Contact();
for( int icount=0 ; icount< itemArray1.Length; icount++ )
{
NotesItem searchedNotesItem =
(Domino.NotesItem)itemArray1.GetValue( icount );
string FieldName = searchedNotesItem.Name.ToString();
//For FirstName
if( searchedNotesItem.Name == "FirstName" )
contact.FirstName= searchedNotesItem.Text;
//For LastName
if( searchedNotesItem.Name == "LastName" )
contact.LastName = searchedNotesItem.Text;
//For Office Phone Number
if( searchedNotesItem.Name == "OfficePhoneNumber" )
contact.OfficePhoneNumber = searchedNotesItem.Text;
if( searchedNotesItem.Name == "InternetAddress" )
contact.EmailId = searchedNotesItem.Text;
}//end for
contactsList.Add( contact );
break;
}//End if
}
}
//Get the nth entry of the selected view according to the iteration.
NotesViewEntry viewEntry = notesViewCollection.GetNextEntry(viewEntry);
}
Why are you asking the user to provide his Employee ID? You should ask him to provide his Notes username (either FullName or ShortName), or his email address. Any of those can be looked up very quickly in the $Users view in names.nsf, giving you fast access to the document containing all the data that you need.
Note: I'm aware that some companies actually enter their Employee ID into the ShortName field in names.nsf. If that's the case for your organization, then what you should be doing is opening a NotesView object using the NotesDatabase.getView() method, and then use the NotesView.getDocumentByKey() method to get the document for the user. E.g., something like this:
NotesView usersView = namesDb.getView("$Users");
NotesDocument userDoc = usersView.getDocumentByKey(employeeId);
Then just read the data that you want, using userDoc.getItemValue() for each information field that you are interested in. You should only do a loop through the entire userdoc.Items array if you are really trying to capture everything, including a bunch of internal-use values.