Hi there I have a database of users who are able to receive emails for specific servers.
Table Ex:
UserName Server1 Server2 Server3
Jane.Doe True False True
Let me explain what exactly is happening, so I set a true or false to the server if they can receive emails or not for specific server.
Now a user submits a log and they pick a server they are reporting to. The Admins of that server will get an email.
However I don't want to hard code in (My boss got mad at me):
Example:
if (logSubmission.curServer == "Server1")
{
email = db.Users.Where(m => m.Server1 == true).Select(m => m.UserName).ToList();
}
So what I thought I could do is:
IEnumerable<SelectListItem> server = db.servers.Select(m => new SelectListItem
{
Value = m.server1,
Text = m.server1
});
string serverFixed = "";
foreach (var item in server)
{
if (item.Text == logSubmission.curServer)
{
serverFixed = item.Text;
serverFixed = serverFixed.Replace(" ", string.Empty);
}
}
I had a table called servers which holds all the servers.
So I call that and create a list of SelectListItem.
I take the Text of the server and match it with what the current server we are looking at.
Once That is done I had to remove spaces because some servers were like this: "Server 2"
And since my User Table Has it as "Sever2" I needed to remove spaces to match it.
Now I need to find a way to get that variable serverFixed into a similar look as:
email = db.Users.Where(m => m.Server1 == true).Select(m => m.UserName).ToList();
I feel like if I can change the .Where to somehow match the serverFixed I could do it.
Any ideas what I could do? Thanks!
Your Table is already hard coded. I'm not sure if that was a good decision to use several fields for servers instead of one foreign key field to a catalogue table with all servers. But maybe you had some reasons for it. I believe it's impossible to use some kind of variable for a field in LINQ. So I would propose to create an enum with all servers like
public enum ServerEnum {server1, server2, ...}
and than you can use it to identify your servers:
switch(logSubmission.curServer)
{
case (ServerEnum.Server.ToString()):
email = db.Users.Where(m => m.Server1 == true).Select(m => m.UserName).ToList();
break;
...
}
But anyway if you could change a structure of your tables that would be a better decision.
Related
I am very new to C# lists and databases, please keep this in mind.
I have a list of workouts saved in a database that also has the UserID field to make each workout added to the table unique to each user. I want to make a list view for when the user logs in, they can see only their workouts.
I have tried to do this by creating a new list without all the workouts that don't have that User's primary key/userID
public void Read()
{
using (UserDataContext context = new UserDataContext())
{
DatabaseWorkouts = context.Workouts.ToList(); // Saves the users from the database into a list
// DatabaseWorkouts = context.Workouts.FindAll(item => item.UserID != Globals.primaryKey); I thought this would work
foreach (var item in DatabaseWorkouts.ToList())
{
if (DatabaseWorkouts.Exists(item => item.UserID != Globals.primaryKey))
{
DatabaseWorkouts.Remove(item);
}
}
ItemList.ItemsSource = DatabaseWorkouts; //Displays the list on the listview in the GUI
}
}
I have run many tests with this code above and I think that it only displays the workouts that are most recent and accept conditions, instead of just accepting conditions.
Please help
Instead of fetching all the workouts and then removing the ones that don't belong to the user, you could just directly fetch the user's ones.
Assuming that Globals.primaryKey is the targeted user's id, you can do the following
var userWorkouts = context.Workouts.Where(w => w.UserId == Globals.primaryKey).ToList();
ItemList.ItemsSource = userWorkouts;
I have the following code to search the global address book by a certain string:
"CONF"
var esb = new ExchangeServiceBinding();
esb.Url = #"https://myurl.com/EWS/Exchange.asmx";
esb.Credentials = new NetworkCredential(_user,_pwd, _domain);
var rnType = new ResolveNamesType {ReturnFullContactData = true, UnresolvedEntry = "CONF"};
ResolveNamesResponseType response = esb.ResolveNames(rnType);
ArrayOfResponseMessagesType responses = resolveNamesResponse.ResponseMessages;
var responseMessage = responses.Items[0] as ResolveNamesResponseMessageType;
ResolutionType[] resolutions = responseMessage.ResolutionSet.Resolution;
the issue is that it seems to be doing a "starts with" search so I have a name called:
"CONF-123" it will show up but if i have a name "JOE-CONF" then it will not.
How can I do a partial string search on this line
var rnType = new ResolveNamesType {ReturnFullContactData = true, UnresolvedEntry = "CONF-"};
i was hoping there was something like:
var rnType = new ResolveNamesType {ReturnFullContactData = true, UnresolvedEntry = "%CONF-%"};
but that doesn't seem to work.
EDIT: Jan 4,2016 - Added sample code for searching AD.
What won't work
Searching the GAL via ResolveNames always uses prefix-string match for Ambiguous Name Resolution (ARN). Although EWS documentation does not say this explicitly, the Exchange ActiveSync documentation does. EWS and Exchange ActiveSync are just protocols; they both rely on ARN underneath, so you are stuck with prefix match, whether you use ActiveSync protocol or EWS.
Here is the relevant quote from Exchange ActiveSync documentation (https://msdn.microsoft.com/en-us/library/ee159743%28v=exchg.80%29.aspx)
The text query string that is provided to the Search command is used
in a prefix-string match
.
What will work
The best thing to do depends on your use case, but here are some ideas:
Search Active Directory in your client program (the program that contains the code you showed in your question)
Set up your own service to search the GAL. Your client program would connect both to Exchange and to your service. Or your service could proxy EWS, so that the client program needs to connect only to your service.
How would you service get the GAL data? One way would be to use EWS ResolveNames repeatedly, to get the GAL data, 100 entries at a time and cache this data in your service. First, retrieve all the "a"s, then all the "b"s, etc. Of course, there can be more than 100 "a"s in the GAL, so just getting all the "a"s could take multiple searches - you would construct your next search string, based on the last entry returned from each search. This can be slow and painful. You would probably want to cache this data in a database and refresh it periodically.
You can also get to GAL through MAPI. You can use MAPI directly (https://msdn.microsoft.com/en-us/library/cc765775%28v=office.12%29.aspx) or through a helper library like Redemption (http://www.dimastr.com/redemption/home.htm). Whether you use MAPI directly or through Redemption, you will need to install Outlook (or Exchange) on the computer where your code is running. Because of this restriction, it may be best to not use MAPI in your client program, but to stick it in a service running on some server and have your client program connect to that service.
AD Code Sample
Another answer provided sample code to search Active Directory. I am adding a code sample that may be better suited for generic use by people who may find this question through search. Compared to the other sample, the code below has the following improvements:
If the search string contains any special characters (like parenthesis), they are escaped, so that the constructed filter string is valid.
Searching by just samaccountname many not be sufficient. If "David Smith" has account name "dsmith", searching for "David" by samaccountname would not find him. My sample shows how to search by more fields and gives some of the fields one may want to search.
Starting at a root like "GC:" is more robust than trying to construct an LDAP entry from Domain.GetComputerDomain().
All IDisposables must be disposed of (usually by using them in a using construct).
// Search Active Directory users.
public static IEnumerable<DirectoryEntry> SearchADUsers(string search) {
// Escape special characters in the search string.
string escapedSearch = search.Replace("*", "\\2a").Replace("(", "\\28")
.Replace(")", "\\29").Replace("/", "\\2f").Replace("\\", "\\5c");
// Find entries where search string appears in ANY of the following fields
// (you can add or remove fields to suit your needs).
// The '|' characters near the start of the expression means "any".
string searchPropertiesExpression = string.Format(
"(|(sn=*{0}*)(givenName=*{0}*)(cn=*{0}*)(dn=*{0}*)(samaccountname=*{0}*))",
escapedSearch);
// Only want users
string filter = "(&(objectCategory=Person)(" + searchPropertiesExpression + "))";
using (DirectoryEntry gc = new DirectoryEntry("GC:")) {
foreach (DirectoryEntry root in gc.Children) {
try {
using (DirectorySearcher s = new DirectorySearcher(root, filter)) {
s.ReferralChasing = ReferralChasingOption.All;
SearchResultCollection results = s.FindAll();
foreach (SearchResult result in results) {
using (DirectoryEntry de = result.GetDirectoryEntry()) {
yield return de;
}
}
}
} finally {
root.Dispose();
}
}
}
}
Though wildcard search isn't possible in EWS, it is possible in AD search. AD Queries support wildcards. i.e., *CONF* can be searched in AD, which will return all results which contain "CONF". Based on the results, query EWS for an corresponding Exchange Object. You need to find a parameter with which you can find the corresponding EWS entry. I guess email address (username) should be sufficient to find the corresponding exchange object.
AD Search code snippet...
private SearchResultCollection SearchByName(string username, string password, string searchKeyword)
{
DirectorySearcher ds = new DirectorySearcher(new DirectoryEntry("LDAP://" + Domain.GetComputerDomain().ToString().ToLower(), username, password));
ds.Filter = "(&((&(objectCategory=Person)(objectClass=User)))(samaccountname=*" + searchKeyword + "*))";
ds.SearchScope = SearchScope.Subtree;
ds.ServerTimeLimit = TimeSpan.FromSeconds(90);
return ds.FindAll();
}
AD Query Example here.
An ambiguous search on an indexed text field can only be done with prefix (or suffix...). That's why Exchange probably implements the query as LIKE 'CONF%'.
I've looked over the documentation and there isn't any way you can bypass it - full table scan (which would have to be the case for %CONF%) does not seem to make sense.
I have also been trying to search the GAL from PHP using php-ews. On a web page the user starts to enter the name to be searched, once 2 chars have been entered a call to read_contacts.php is made passing the 2 chars entered. read_contacts.php uses EWS ResolveNames request to retrieve a limited list of contacts. The code then filters on some criteria and also checks that the displayName starts with the chars entered. This then returns the filtered list:
<?php
$staffName = $_GET['q'];
require_once './php-ews/EWSType.php';
require_once './php-ews/ExchangeWebServices.php';
require_once 'php-ews/EWSType/RestrictionType.php';
require_once 'php-ews/EWSType/ContainsExpressionType.php';
require_once 'php-ews/EWSType/PathToUnindexedFieldType.php';
require_once 'php-ews/EWSType/ConstantValueType.php';
require_once 'php-ews/EWSType/ContainmentModeType.php';
require_once 'php-ews/EWSType/ResolveNamesType.php';
require_once 'php-ews/EWSType/ResolveNamesSearchScopeType.php';
require_once 'php-ews/NTLMSoapClient.php';
require_once 'php-ews/NTLMSoapClient/Exchange.php';
$host = '[exchange server]';
$user = '[exchange user]';
$password = '[exchange password]';
$ews = new ExchangeWebServices($host, $user, $password);
$request = new EWSType_ResolveNamesType();
$request->ReturnFullContactData = true;
$request->UnresolvedEntry = $staffName;
$displayName = '';
$i = 0;
$staff_members = false;
$response = $ews->ResolveNames($request);
if ($response->ResponseMessages->ResolveNamesResponseMessage->ResponseClass == 'Error' && $response->ResponseMessages->ResolveNamesResponseMessage->MessageText == 'No results were found.') {
}
else {
$numNamesFound = $response->ResponseMessages->ResolveNamesResponseMessage->ResolutionSet->TotalItemsInView;
$i2=0;
for ($i=0;$i<$numNamesFound;$i++) {
if ($numNamesFound == 1) {
$displayName = $response->ResponseMessages->ResolveNamesResponseMessage->ResolutionSet->Resolution->Contact->DisplayName;
}
else {
$displayName = $response->ResponseMessages->ResolveNamesResponseMessage->ResolutionSet->Resolution[$i]->Contact->DisplayName;
}
echo "DisplayName: " . $displayName . "\n";
if (stripos($displayName, 'External') == true) {
}
else {
$searchLen = strlen($staffName);
if (strcasecmp(substr($displayName, 0, $searchLen), $staffName) == 0) {
if ($numNamesFound == 1) {
$emailAddress = $response->ResponseMessages->ResolveNamesResponseMessage->ResolutionSet->Resolution->Mailbox->EmailAddress;
}
else {
$emailAddress = $response->ResponseMessages->ResolveNamesResponseMessage->ResolutionSet->Resolution[$i]->Mailbox->EmailAddress;
}
$staff_members[$i2] = array( 'name' => $displayName,'email' => $emailAddress );
$i2++;
}
}
}
$staffJson = json_encode($staff_members);
echo $staffJson;
}
Most of the time this seems to work, ie: 'mi', 'jo' returns Mike, Michael, or Joe, John, except when I sent 'Si' or 'si', for Simon, then the ResolveNames call returns the first 100 entries from the GAL.
For the time being I have increased the minimum number of characters to be entered to 3, ie: 'sim' and this works. The problem will be when we get staff with only 2 character first names.
I just thought I would share the code to see if it helps and to see if anyone knows why my 'si' does not work properly.
I am accessing Exchange 2010
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.
Using the query function of entity collection in C# and it takes a long time to load the related records back from SQL Server 2008. Is there any fast way to do this? This is the query function I use:
public void SearchProducts()
{
//Filter by search string array(searchArray)
List<string> prodId = new List<string>();
foreach (string src in searchArray)
{
StoreProductCollection prod = new StoreProductCollection();
prod.Query.Where(prod.Query.StptName.ToLower() == src.ToLower() && prod.Query.StptDeleted.IsNull());
prod.Query.Select(prod.Query.StptName, prod.Query.StptPrice, prod.Query.StptImage, prod.Query.StptStoreProductID);
// prod.Query.es.Top = 4;
prod.Query.Load();
if (prod.Count > 0)
{
foreach (StoreProduct stpt in prod)
{
if (!prodId.Contains(stpt.StptStoreProductID.ToString().Trim()))
{
prodId.Add(stpt.StptStoreProductID.ToString().Trim());
productObjectsList.Add(stpt);
}
}
}
}
You're hitting the database once per searchArray item, this is very wrong.
You might get better performance like this (have no way of testing it, give it a shot):
public void SearchProducts()
{
//Filter by search string array(searchArray)
List<string> prodId = new List<string>();
StoreProductCollection prod = new StoreProductCollection();
// Notice that your foreach() is gone
// replace this
// prod.Query.Where(prod.Query.StptName.ToLower() == src.ToLower() && prod.Query.StptDeleted.IsNull());
// with this (or something similar: point is, you should call .Load() exactly once)
prod.Query.where(prod.Query.StptDeleted.IsNull() && src.Any(srcArrayString => prod.Query.StptName.ToLower()==srcArrayString.ToLower());
prod.Query.Select(prod.Query.StptName, prod.Query.StptPrice, prod.Query.StptImage, prod.Query.StptStoreProductID);
// prod.Query.es.Top = 4;
prod.Query.Load();
// ... rest of your code follows.
}
Given List<string> searchArray containing lowered words :
public void SearchProducts()
{
//Filter by search string array(searchArray)
List<string> prodId = new List<string>();
StoreProductCollection prod = new StoreProductCollection();
prod.Query.Where(searchArray.Contains(prod.Query.StptName.ToLower()) && prod.Query.StptDeleted.IsNull());
prod.Query.Select(prod.Query.StptName, prod.Query.StptPrice, prod.Query.StptImage, prod.Query.StptStoreProductID);
// prod.Query.es.Top = 4;
prod.Query.Load();
if (prod.Count > 0)
{
foreach (StoreProduct stpt in prod)
{
if (!prodId.Contains(stpt.StptStoreProductID.ToString().Trim()))
{
prodId.Add(stpt.StptStoreProductID.ToString().Trim());
productObjectsList.Add(stpt);
}
}
}
}
This way you have only one query for all words.
First of all, put an index on StptName column.
Second, if you need even better performance, write a Stored Procedure in SQL, to do your querying, and map it with Entity Framework.
Let me know if you need explanation on how to do any of the above.
A couple more micro-optimizations you can do if you don't want to write a Stored Procedure:
Write src.ToLower() in a temporary varaible, and than compare prod.Query.StptName.ToLower() to it.
By default, SQL Server queries are case insensitive, so check if that's the case, and if so, you can get rid of the ToLower altogether. You can change case sensitivity through Collation.
EDIT:
To create an Index:
Open the table designer in SQL Server Managment Studio.
Right click anywhere and select Indexes/Keys.
Click Add.
Under Columns add StptName.
Under Is Unique specify whether StptName is unique or not.
Under type select "index".
That's all!
As for mapping stored procedures - here's a nice tutorial:
http://www.robbagby.com/entity-framework/entity-framework-modeling-select-stored-procedures/
(You can jump straight to the "Map in the Select Stored Procedure" Section).
To be more precise I will be working with example...
Clean query is: (type:77 AND (zipCode:12345 OR name:OR))
When querying on Solr Admin page this throws exception:
org.apache.lucene.queryParser.ParseException: Cannot parse...
So on Solr Admin page I changed query to:
(type:"77" AND (zipCode:"12345" OR name:"OR"))
which worked as a charm
Now I have a problem to do the same stuff with solrnet. I am using SolrQueryByField class for querying. When I am working with
new SolrQueryByField("name", "OR")
I get Solrnet.Exceptions.InvalidFieldException which is in accordance with Solr Admin page, but when I am working with
new SolrQueryByField("name", "\"OR\"")
I get wrong results. By inspecting network traffic I found out that http get request is different (for brevity only name field name and value are given):
name%3A%22OR%22 => from Solr Admin page
name%3a%5c%22OR%5c%22 => from solrnet
My question is: what sholud I do to prevent solrnet from adding %5C (backslash) to query string?
Thanks in advance
SolrQueryByField produces quoted/escaped values. If you have some special case where this is not desirable (such as this case) you can use SolrQuery, e.g. :
Query.Field("type").Is(77) && (Query.Field("zipCode").Is("12345") || Query.Simple("name:\"OR\""))
Please try to pass the string array that contains multiple field names and search text in the below method. It will return the solrnet query for search with multiple filed name with OR condition.
public ISolrQuery BuildQuery(string[] SearchFields, string SearchText)
{
try
{
AbstractSolrQuery firstQuery = new SolrQueryByField(SearchFields[0], SearchText) { Quoted = false };
for (var i = 1; i < parameters.SearchFields.Length; i++)
{
firstQuery = firstQuery || new SolrQueryByField(SearchFields[i], SearchText) { Quoted = false };
}
return firstQuery;
}