I have some SQL request:
go
Update dbo.Parameter set ValueAsStr = '{
"CreateDepoUrl": "https://sandbox.sg...../",
"CheckDepoStatusUrl": "https://sandbox.sg..../",
"CreatePayoutUrl": "https://sandbox.sg....../",
"CheckPayoutStatusUrl": "https://sandbox.sg..../",
"PayoutTerminalIds": {
....
go
If I send this request in SSMS all is alright
My method for send SQL request from C# code:
public static void SendToMainSqlRequest(MainDbContext mainDbContext, string queryString)
{
using (var conn = mainDbContext.Database.GetDbConnection())
{
conn.Open();
var command = mainDbContext.Database.GetDbConnection().CreateCommand();
command.CommandText = queryString;
command.CommandType = CommandType.Text;
int number = command.ExecuteNonQuery();
Console.WriteLine("count of updates: {0}", number);
conn.Close();
}
}
When I send request in C# code I get an exception:
Incorrect syntax near '.'
If I delete "dbo." in the SQL request I get an exception:
Incorrect syntax near '='
Table and field names are correct. Without typos.
How I can solve this problem?
Thanks!
I use the code bellow to do this :
...
var lines = GoSplitter.Split(queryString);
foreach(var line in lines)
{
command.CommandText = line;
command.CommandType = CommandType.Text;
int number = command.ExecuteNonQuery();
// process number if needed
}
...
The class GoSplitter (sorry comments are in french):
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
namespace DatabaseAndLogLibrary.DataBase
{
/// <summary>
/// Coupe un texte SQL en fonction des GO
/// Prend en compte les Go dans les chaines ou les commentaires SQL qui eux doivent être ignorés
/// Retire aussi les instruction SQL : USE
/// </summary>
internal class GoSplitter
{
/// <summary>
/// Pour détection des instruction USE
/// </summary>
private static Regex regUse = new Regex(#"^\s*USE\s");
/// <summary>
/// Renvoie la liste des instructions SQL séparé en fonction des GO dans le script d'origine
/// Prend en compte les Go dans les chaines ou les commentaires SQL qui eux doivent être ignorés
/// </summary>
/// <param name="fileContent"></param>
/// <returns></returns>
public static IEnumerable<string> Split(string fileContent)
{
if (string.IsNullOrWhiteSpace(fileContent))
{
yield break;
}
string res;
var currentState = EState.Normal;
List<Marker> markers = LoadMarker(fileContent).OrderBy(x => x.Index).ToList();
int index0 = 0;
for (int i = 0; i < markers.Count; i++)
{
switch (currentState)
{
case EState.Normal:
switch (markers[i].Event)
{
case EMarker.Go:
res = fileContent.Substring(index0, markers[i].Index - index0).Trim();
res = ReplaceUse(res);
if (!string.IsNullOrWhiteSpace(res))
{
yield return res;
}
index0 = markers[i].Index + 2; // 2 lettres dans go
break;
case EMarker.Quote:
currentState = EState.InText;
break;
case EMarker.Comment:
currentState = EState.InComment;
break;
}
break;
case EState.InText:
if (markers[i].Event == EMarker.Quote)
{
currentState = EState.Normal;
}
break;
case EState.InComment:
if (markers[i].Event == EMarker.EndComment)
{
currentState = EState.Normal;
}
break;
}
}
res = fileContent.Substring(index0, fileContent.Length - index0).Trim();
res = ReplaceUse(res);
if (!string.IsNullOrWhiteSpace(res))
{
yield return res;
}
}
/// <summary>
/// Charge les points clés du script
/// </summary>
/// <param name="fileContent"></param>
/// <returns></returns>
private static IEnumerable<Marker> LoadMarker(string fileContent)
{
var regGo = new Regex(#"\bgo\b", RegexOptions.Multiline | RegexOptions.IgnoreCase);
foreach(var m in regGo.Matches(fileContent).Where(x => x.Success).Select(x => new Marker() { Index = x.Index, Event = EMarker.Go }))
{
yield return m;
}
var regQuote = new Regex(#"'", RegexOptions.Multiline);
foreach (var m in regQuote.Matches(fileContent).Where(x => x.Success).Select(x => new Marker() { Index = x.Index, Event = EMarker.Quote }))
{
yield return m;
}
var regComment1 = new Regex(#"-(-)+[\s\S]*?$", RegexOptions.Multiline);
foreach (Match m in regComment1.Matches(fileContent).Where(x => x.Success))
{
yield return new Marker() { Index = m.Index, Event = EMarker.Comment };
yield return new Marker() { Index = m.Index + m.Length, Event = EMarker.EndComment };
}
var regComment2 = new Regex(#"/\*[\s\S]*?\*/", RegexOptions.Multiline);
foreach (Match m in regComment2.Matches(fileContent).Where(x => x.Success))
{
yield return new Marker() { Index = m.Index, Event = EMarker.Comment };
yield return new Marker() { Index = m.Index + m.Length, Event = EMarker.EndComment };
}
}
/// <summary>
/// Remplace les instructions using
/// </summary>
/// <param name="sqlLine"></param>
/// <returns></returns>
private static string ReplaceUse(string sqlLine)
=> regUse.Replace(sqlLine, string.Empty); // .Replace("USE", "---");
[DebuggerDisplay("{Index} - {Event}")]
private class Marker
{
public int Index {get; set;}
public EMarker Event { get; set; }
}
/// <summary>
/// les types de détection qui aggissent sur l'automate
/// </summary>
private enum EMarker
{
Go,
Quote,
Comment,
EndComment
}
/// <summary>
/// Les états de l'automate
/// </summary>
private enum EState
{
Normal,
InComment,
InText
}
}
}
Enjoy!
You can use the SQL Server Management Objects library to execute SQL commands with GO statements. We use this internally for executing database migration scripts to update our database schema.
Here's the library.
And some sample code demonstrating how to use it:
using System;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.Data.SqlClient;
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var server = new Server(new ServerConnection(connection));
//this is to get script output (if you have any)
server.ConnectionContext.ServerMessage += (sender, eventArgs) =>
{
Console.WriteLine(eventArgs.Error.Message);
};
server.ConnectionContext.ExecuteNonQuery("some SQL with GO statements");
}
Related
this program going loop
and not resulting files with the data
namespace csv1
{
class A
{
public static async Task<List<string>> sql_to_csv(string connectionString, string providerName, string tableName, string outputFilepath, Encoding outputFileEncoding, long maxNbOfRows, bool addHeaderRow = true, string separator = ";")
{
DbProviderFactory factory = DbProviderFactories.GetFactory(providerName);
using (DbConnection connection = factory.CreateConnection())
{
connection.ConnectionString = connectionString;
await connection.OpenAsync();
DbCommand command = connection.CreateCommand();
command.CommandText = "SELECT top 10 * FROM table";
long totalRecords = 0;
long fileCount = 0;
List<string> filesCreated = new List<string>();
string filePattern = calculateFilePattern(outputFilepath);
using (var reader = await command.ExecuteReaderAsync())
{
// if (reader == null || !reader.HasRows)
// return filesCreated;//Table is empty
while (reader.HasRows)
{
string curFileName = string.Format(filePattern,++fileCount);
using (var writer = new System.IO.StreamWriter(curFileName))
{
totalRecords += await _exportToSplittedCsv(reader, writer, maxNbOfRows, addHeaderRow, separator) ;
}
filesCreated.Add(filePattern);
}
}
//You can return totalRecords or the list of files created or event the fileCount if you prefer
return filesCreated;
}
}
private static string calculateFilePattern(string path)
{
//The path already contains the Counter placeHolder on it
if (path.Contains("{0"))
return path;
int extIndex = path.LastIndexOf('.');
if (extIndex == -1)
return path + "{0:00000}";
else
return path.Substring(0, extIndex) + "{0:00000}" + path.Substring(extIndex);
}
/// <summary>
/// Exports Database until reader has no more records or until maxNbOfRows is reached
/// </summary>
/// <param name="reader">System.Data.Common.DbDataReader used to read data from database</param>
/// <param name="writer">Writer that will write CSV content in the current file</param>
/// <param name="maxNbOfRows">Maximum number of rows to write on the output file</param>
/// <param name="addHeaderRow">When true: first row in the CSV file will contain the Column names</param>
/// <param name="separator">CSV field separator</param>
/// <returns></returns>
private static async Task<long> _exportToSplittedCsv(System.Data.Common.DbDataReader reader, System.IO.StreamWriter writer, long maxNbOfRows, bool addHeaderRow, string separator)
{
long rowCount = 0;
if (addHeaderRow)
{
await writer.WriteAsync(getColNames(reader, separator));
await writer.WriteLineAsync();
}
while (await reader.ReadAsync() && (maxNbOfRows < 1 || rowCount < maxNbOfRows))
{
// await writer.WriteLineAsync();
await writer.WriteLineAsync(buildCsvRow(reader, separator));
++rowCount;
}
await writer.FlushAsync();
writer.Close();
return rowCount;
}
/// <summary>
/// Builds CSV row containing the column names
/// </summary>
/// <param name="reader">System.Data.Common.DbDataReader used to read data from database</param>
/// <param name="separator">CSV field separator</param>
/// <returns></returns>
private static string getColNames(System.Data.Common.DbDataReader reader, string separator)
{
var rowBuilder = new StringBuilder();
for (int i = 0; i < reader.FieldCount; i++)
{
rowBuilder.Append(separator).Append(reader.GetName(i));
}
//We remove extra separator from the begin...
return rowBuilder.Remove(0, 1).ToString();
}
/// <summary>
/// Builds a CSV row containing a database row
/// </summary>
/// <param name="reader">System.Data.Common.DbDataReader used to read data from database</param>
/// <param name="separator">CSV field separator</param>
/// <returns></returns>
private static string buildCsvRow(System.Data.Common.DbDataReader reader, string separator)
{
var rowBuilder = new StringBuilder();
for (int i = 0; i < reader.FieldCount; i++)
{
//If you want to format DateTime to a specific value...
/*switch (reader.GetDataTypeName(i))
{
case : //The typename changes depending of the Database type...
DateTime dt = reader.GetDateTime(i);
if (dt == null)
rowBuilder.Append(separator);
else
rowBuilder.Append(separator).Append(dt.ToString("yyyy-MM-dd HH:mm:ss"));
break;
default:
rowBuilder.Append(separator).Append(reader.GetValue(i).ToString());
}*/
rowBuilder.Append(separator).Append(reader.GetValue(i).ToString());
}
//We remove extra separator from the begin...
return rowBuilder.Remove(0, 1).ToString();
}
static void Main(string[] args)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
// replace these with your own values
builder.DataSource = "xxxxx";
builder.InitialCatalog = "xxxxxx";
builder.IntegratedSecurity = true;
string provider = "System.Data.SqlClient";
Task task = sql_to_csv(builder.ConnectionString, provider, "table", "C:\\export_customerOrders{0:00000}.csv", Encoding.Default, 5, true, ",");
task.Wait(360);
}
}
}
In my code, I create a DLL dynamically. The following code is what I use to create my DLL:
/// <summary>
/// See CodeDom example hon how its used
/// </summary>
/// <param name="source_code"></param>
/// <param name="assemblies"></param>
/// <param name="generate_executable"></param>
/// <param name="compiler_options"></param>
/// <param name="output"></param>
/// <returns></returns>
public static Tuple<Assembly, string> CompileSource(string source_code, List<string> assemblies, bool generate_executable = false, string compiler_options = "/optimize", string output_assembly = "")
{
CompilerParameters compilerParams = null;
CSharpCodeProvider provider = null;
CompilerResults result = null;
string errors = string.Empty;
try
{
provider = new CSharpCodeProvider();
compilerParams = new CompilerParameters();
foreach (var entry in assemblies)
{
compilerParams.ReferencedAssemblies.Add(entry);
}
if (output_assembly != string.Empty)
compilerParams.OutputAssembly = output_assembly;
compilerParams.GenerateExecutable = generate_executable;
compilerParams.GenerateInMemory = false;
compilerParams.IncludeDebugInformation = true;
compilerParams.CompilerOptions = compiler_options;
result = provider.CompileAssemblyFromSource(compilerParams, source_code);
foreach (CompilerError error in result.Errors)
{
errors += String.Format("{0}({1},{2}: error {3}: {4}", error.FileName, error.Line, error.Column, error.ErrorNumber, error.ErrorText);
}
}
catch (Exception err)
{
}
return Tuple.Create(result.CompiledAssembly, errors);
}
What I would like to do is add Assembly Information like the company, product, title, etc.
Looking at this site and site to add it but I'm not sure how to do it.
The CompilerParameters doesn't seem to give an option to add these attributes.
Anyone have a suggestion if it can be done on how to add this information?
Thanks
I'm building an app based on the Gmail API. I can see all messages from the current inbox, but I need to limit this to only messages which have an attachment. How can I do this?
This is my GoogleController.cs:
[HttpGet]
[Authorize]
public async Task<IActionResult> GetListEmail(string LabelId, string nameLabel)
{
string UserEmail = User.FindFirst(c => c.Type == ClaimTypes.Email).Value;
var service = GetService();
List<My_Message> listMessages = new List<My_Message>();
List<Message> result = new List<Message>();
var emailListRequest = service.Users.Messages.List(UserEmail);
emailListRequest.LabelIds = LabelId;
emailListRequest.IncludeSpamTrash = false;
emailListRequest.MaxResults = 1000;
var emailListResponse = await emailListRequest.ExecuteAsync();
if (emailListResponse != null && emailListResponse.Messages != null)
{
foreach (var email in emailListResponse.Messages)
{
var emailInfoRequest = service.Users.Messages.Get(UserEmail, email.Id);
var emailInfoResponse = await emailInfoRequest.ExecuteAsync();
if (emailInfoResponse != null)
{
My_Message message = new My_Message();
message.Id = listMessages.Count + 1;
message.EmailId = email.Id;
foreach (var mParts in emailInfoResponse.Payload.Headers)
{
if (mParts.Name == "Date")
message.Date_Received = mParts.Value;
else if (mParts.Name == "From")
message.From = mParts.Value;
else if (mParts.Name == "Subject")
message.Title = mParts.Value;
}
listMessages.Add(message);
}
}
}
ViewBag.Message = nameLabel;
return View("~/Views/Home/Index.cshtml", listMessages);
}
I think a simpler solution would be to query in the Users.messages.list endpoint without the need to create filters.
You can actually use the parameter q to make a query like you would in the GMail searchbox, if not familiar you can look at the whole list of operators.
In fact there is an example to make use of this query parameter in the documentation:
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using System.Collections.Generic;
// ...
public class MyClass {
// ...
/// <summary>
/// List all Messages of the user's mailbox matching the query.
/// </summary>
/// <param name="service">Gmail API service instance.</param>
/// <param name="userId">User's email address. The special value "me"
/// can be used to indicate the authenticated user.</param>
/// <param name="query">String used to filter Messages returned.</param>
public static List<Message> ListMessages(GmailService service, String userId, String query)
{
List<Message> result = new List<Message>();
UsersResource.MessagesResource.ListRequest request = service.Users.Messages.List(userId);
request.Q = query; // inform this with the right query
do
{
try
{
ListMessagesResponse response = request.Execute();
result.AddRange(response.Messages);
request.PageToken = response.NextPageToken;
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
} while (!String.IsNullOrEmpty(request.PageToken));
return result;
}
// ...
}
So for your case you just need to add the line
emailListRequest.Q = "has:attachment";
before executing the request, doing like this will not create a whole filter for your account, so maybe it's more convenient for your case.
use a filter with criteria like this => criteria.query="has:attachment"
see also here => https://developers.google.com/gmail/api/v1/reference/users/settings/filters#resource
For testing any filter you can use GMail:
image
I tried to logged in localhost and everything works properly.
The code:
public static bool LoginGoogleCalendar(string clientId, string clientSecret, string idGCalendarUser, string calendarServiceScope, string folder)
{
try
{
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = clientId,
ClientSecret = clientSecret,
},
new[] { calendarServiceScope },
idGCalendarUser,
CancellationToken.None, new FileDataStore(folder)).Result;
return true;
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
return false;
}
}
(I set properly the authorization for fileDataStore)
In Google Developers Console:
Redirect URIs: http://localhost/authorize/
Javascript Origins: http://localhost:8081
I use Visual Studio 2013, IIS 8
When i try the login to the server, will block the entire server for minutes and the answer after is: System.Web.HttpException Request timed out.
In Google Developers Console:
Redirect URIs: http://pippo.pluto.it/authorize/
Javascript Origins: http://pippo.pluto.it
On the server: IIS 7
The reference to my example:
https://developers.google.com/google-apps/calendar/instantiate
I walked into to same problem. Alot of examples on the internet tell you to use this class. For web applications this is not the class to use though. This class wil work perfect for "offline" applications, but when you use this class on the IIS server it wil try to open the popup on the server but it wont let it.
The class I use: GoogleAuthorizationCodeFlow
using Google.Apis.Analytics.v3;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Requests;
using Google.Apis.Auth.OAuth2.Web;
using Google.Apis.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
namespace GoogleOauth2DemoWebApp
{
public class GoogleOauth
{
public AnalyticsService Handle(string _userId, string _connectionString, string _googleRedirectUri, string _applicationName, string[] _scopes)
{
try
{
string UserId = _userId;//The user ID wil be for examlpe the users gmail address.
AnalyticsService service;
GoogleAuthorizationCodeFlow flow;
//use extended class to create google authorization code flow
flow = new ForceOfflineGoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
DataStore = new DbDataStore(_connectionString),//DataStore class to save the token in a SQL database.
ClientSecrets = new ClientSecrets { ClientId = "XXX-YOUR CLIENTID-XXX", ClientSecret = "XXX-YOURCLIENTSECRET-XXX" },
Scopes = _scopes,
});
var uri = HttpContext.Current.Request.Url.ToString();
string redirecturi = _googleRedirectUri;//This is the redirect URL set in google developer console.
var code = HttpContext.Current.Request["code"];
if (code != null)
{
var token = flow.ExchangeCodeForTokenAsync(UserId, code,
uri.Substring(0, uri.IndexOf("?")), CancellationToken.None).Result;
var test = HttpContext.Current.Request["state"];
// Extract the right state.
var oauthState = AuthWebUtility.ExtracRedirectFromState(
flow.DataStore, UserId, HttpContext.Current.Request["state"]).Result;
HttpContext.Current.Response.Redirect(oauthState);
}
else
{
var result = new AuthorizationCodeWebApp(flow, redirecturi, uri).AuthorizeAsync(UserId,
CancellationToken.None).Result;
if (result.RedirectUri != null)
{
// Redirect the user to the authorization server.
HttpContext.Current.Response.Redirect(result.RedirectUri);
}
else
{
// The data store contains the user credential, so the user has been already authenticated.
service = new AnalyticsService(new BaseClientService.Initializer()
{
HttpClientInitializer = result.Credential,
ApplicationName = _applicationName
});
return service;
}
}
return null;
}
catch (Exception ex)
{
throw ex;
}
}
internal class ForceOfflineGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
public ForceOfflineGoogleAuthorizationCodeFlow(GoogleAuthorizationCodeFlow.Initializer initializer) : base(initializer) { }
public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(string redirectUri)
{
var ss = new Google.Apis.Auth.OAuth2.Requests.GoogleAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl));
ss.AccessType = "offline";
ss.ApprovalPrompt = "force";
ss.ClientId = ClientSecrets.ClientId;
ss.Scope = string.Join(" ", Scopes);
ss.RedirectUri = redirectUri;
return ss;
}
};
}
}
Also I user a DataStore class. Saving the tokens to a file on you server isnt the best practice. I used a SQL database.
Example of the datastore class. It will make a table for you, not the best way but for testing perpose good enough.
using Google.Apis.Json;
using Google.Apis.Util.Store;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GoogleOauth2DemoWebApp
{
public class DbDataStore : IDataStore
{
readonly string connectionString;
public string ConnectionString { get { return connectionString; } }
private Boolean _ConnectionExists { get; set; }
public Boolean connectionExists { get { return _ConnectionExists; } }
/// <summary>
/// Constructs a new file data store with the specified folder. This folder is created (if it doesn't exist
/// yet) under the current directory
/// </summary>
/// <param name="folder">Folder name</param>
public DbDataStore(String _connectionString)
{
connectionString = _connectionString;
SqlConnection myConnection = this.connectdb(); // Opens a connection to the database.
if (_ConnectionExists)
{
// check if the Table Exists;
try
{
SqlDataReader myReader = null;
SqlCommand myCommand = new SqlCommand("select 1 from GoogleUser where 1 = 0",
myConnection);
myReader = myCommand.ExecuteReader();
while (myReader.Read())
{
var hold = myReader["Column1"];
}
}
catch
{
// table doesn't exist we create it
SqlCommand myCommand = new SqlCommand("CREATE TABLE [dbo].[GoogleUser]( " +
" [username] [nvarchar](4000) NOT NULL," +
" [RefreshToken] [nvarchar](4000) NOT NULL," +
" [Userid] [nvarchar](4000) NOT NULL" +
" ) ON [PRIMARY]", myConnection);
myCommand.ExecuteNonQuery();
}
}
myConnection.Close();
}
/// <summary>
/// Stores the given value for the given key. It creates a new file (named <see cref="GenerateStoredKey"/>) in
/// <see cref="FolderPath"/>.
/// </summary>
/// <typeparam name="T">The type to store in the data store</typeparam>
/// <param name="key">The key</param>
/// <param name="value">The value to store in the data store</param>
public Task StoreAsync<T>(string key, T value)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}
var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
SqlConnection myConnection = this.connectdb();
if (!_ConnectionExists)
{
throw new Exception("Not connected to the database");
}
// Try and find the Row in the DB.
using (SqlCommand command = new SqlCommand("select Userid from GoogleUser where UserName = #username", myConnection))
{
command.Parameters.AddWithValue("#username", key);
string hold = null;
SqlDataReader myReader = command.ExecuteReader();
while (myReader.Read())
{
hold = myReader["Userid"].ToString();
}
myReader.Close();
if (hold == null)
{
try
{
// New User we insert it into the database
string insertString = "INSERT INTO [dbo].[GoogleUser] ([username],[RefreshToken],[Userid]) " +
" VALUES (#key,#value,'1' )";
SqlCommand commandins = new SqlCommand(insertString, myConnection);
commandins.Parameters.AddWithValue("#key", key);
commandins.Parameters.AddWithValue("#value", serialized);
commandins.ExecuteNonQuery();
}
catch (Exception ex)
{
throw new Exception("Error inserting new row: " + ex.Message);
}
}
else
{
try
{
// Existing User We update it
string insertString = "update [dbo].[GoogleUser] " +
" set [RefreshToken] = #value " +
" where username = #key";
SqlCommand commandins = new SqlCommand(insertString, myConnection);
commandins.Parameters.AddWithValue("#key", key);
commandins.Parameters.AddWithValue("#value", serialized);
commandins.ExecuteNonQuery();
}
catch (Exception ex)
{
throw new Exception("Error updating user: " + ex.Message);
}
}
}
myConnection.Close();
return TaskEx.Delay(0);
}
/// <summary>
/// Deletes the given key. It deletes the <see cref="GenerateStoredKey"/> named file in <see cref="FolderPath"/>.
/// </summary>
/// <param name="key">The key to delete from the data store</param>
public Task DeleteAsync<T>(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}
SqlConnection myConnection = this.connectdb();
if (!_ConnectionExists)
{
throw new Exception("Not connected to the database");
}
// Deletes the users data.
string deleteString = "delete from [dbo].[GoogleUser] " +
"where username = #key";
SqlCommand commandins = new SqlCommand(deleteString, myConnection);
commandins.Parameters.AddWithValue("#key", key);
commandins.ExecuteNonQuery();
myConnection.Close();
return TaskEx.Delay(0);
}
/// <summary>
/// Returns the stored value for the given key or <c>null</c> if the matching file (<see cref="GenerateStoredKey"/>
/// in <see cref="FolderPath"/> doesn't exist.
/// </summary>
/// <typeparam name="T">The type to retrieve</typeparam>
/// <param name="key">The key to retrieve from the data store</param>
/// <returns>The stored object</returns>
public Task<T> GetAsync<T>(string key)
{
//Key is the user string sent with AuthorizeAsync
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
// Note: create a method for opening the connection.
SqlConnection myConnection = new SqlConnection(this.ConnectionString);
myConnection.Open();
// Try and find the Row in the DB.
using (SqlCommand command = new SqlCommand("select RefreshToken from GoogleUser where UserName = #username;", myConnection))
{
command.Parameters.AddWithValue("#username", key);
string RefreshToken = null;
SqlDataReader myReader = command.ExecuteReader();
while (myReader.Read())
{
RefreshToken = myReader["RefreshToken"].ToString();
}
if (RefreshToken == null)
{
// we don't have a record so we request it of the user.
tcs.SetResult(default(T));
}
else
{
try
{
// we have it we use that.
tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(RefreshToken));
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}
}
return tcs.Task;
}
/// <summary>
/// Clears all values in the data store. This method deletes all files in <see cref="FolderPath"/>.
/// </summary>
public Task ClearAsync()
{
SqlConnection myConnection = this.connectdb();
if (!_ConnectionExists)
{
throw new Exception("Not connected to the database");
}
// Removes all data from the Table.
string truncateString = "truncate table [dbo].[GoogleUser] ";
SqlCommand commandins = new SqlCommand(truncateString, myConnection);
commandins.ExecuteNonQuery();
myConnection.Close();
return TaskEx.Delay(0);
}
/// <summary>Creates a unique stored key based on the key and the class type.</summary>
/// <param name="key">The object key</param>
/// <param name="t">The type to store or retrieve</param>
public static string GenerateStoredKey(string key, Type t)
{
return string.Format("{0}-{1}", t.FullName, key);
}
//Handel's creating the connection to the database
private SqlConnection connectdb()
{
SqlConnection myConnection = null;
try
{
myConnection = new SqlConnection(this.ConnectionString);
try
{
myConnection.Open();
// ensuring that we are able to make a connection to the database.
if (myConnection.State == System.Data.ConnectionState.Open)
{
_ConnectionExists = true;
}
else
{
throw new ArgumentException("Error unable to open connection to the database.");
}
}
catch (Exception ex)
{
throw new ArgumentException("Error opening Connection to the database: " + ex.Message);
}
}
catch (Exception ex)
{
throw new ArgumentException("Error creating Database Connection: " + ex.Message);
}
return myConnection;
}
}
}
using the class:
GoogleOauth g = new GoogleOauth();
AnalyticsService service = g.Handle(userEmailAddress,
connectionString, redirectUrl,
"YOURAPLICATIONNAME",
new[] {AnalyticsService.Scope.AnalyticsReadonly});
DataResource.RealtimeResource.GetRequest request = service.Data.Realtime.Get(String.Format("ga:{0}", profileId), "rt:activeUsers");
RealtimeData feed = request.Execute();
If people are intrested I can upload a sample project to github.
I am attempting to retrieve the most recent date that my users have logged into any Google app service such as Gmail (or had this done for them via mobile device/imap/pop etc). I am an administrator of the domain on Google apps for Education and using the C# gdata-api.
I would like to recycle student usernames if they have been gone for longer than a year without signing into their gmail apps and require the last accessed date to accomplish this.
Please let me know if this is possible
Thank you.
I used the gmail reporting API. I forget where I got this from but I stole it from somewhere. If you know where, please put a comment link.
/// <summary>
/// This contains the logic for constructing and submitting a report
/// request to the Google Apps reporting service and reading the response.
///
/// The description of the web service protocol can be found at:
///
/// http://code.google.com/apis/apps/reporting/google_apps_reporting_api.html
///
/// Example usage:
/// Get the latest accounts report to standard output.
/// client.getReport("accounts", null, null);
/// Get the accounts report for May 15, 2007 and save it to out.txt.
/// client.getReport("accounts", "2007-05-15", "out.txt");
/// </summary>
public class ReportsManager
{
/// <summary>
/// URL to POST to obtain an authentication token
/// </summary>
private const string CLIENT_LOGIN_URL =
"https://www.google.com/accounts/ClientLogin";
/// <summary>
/// URL to POST to retrive resports from the API
/// </summary>
private const string REPORTING_URL =
"https://www.google.com/hosted/services/v1.0/reports/ReportingData";
/// <summary>
/// Date format of the API
/// </summary>
private const string DATE_FORMAT = "yyyy-MM-dd";
/// <summary>
/// Hour of the day when the API data gets published
/// </summary>
private const int PUBLISH_HOUR_OF_DAY = 13; // Publish hour + 1 hour;
/// <summary>
/// Time diference to UTC
/// </summary>
private const int PUBLISH_TIME_DIFERENCE_TO_UTC = -8;
/// <summary>
/// Email command-line argument
/// </summary>
private const string EMAIL_ARG = "email";
/// <summary>
/// Password command-line argument
/// </summary>
private const string PASSWORD_ARG = "password";
/// <summary>
/// Domain command-line argument
/// </summary>
private const string DOMAIN_ARG = "domain";
/// <summary>
/// Report command-line argument
/// </summary>
private const string REPORT_ARG = "report";
/// <summary>
/// Date command-line argument
/// </summary>
private const string DATE_ARG = "date";
/// <summary>
/// Output File command-line argument
/// </summary>
private const string OUT_FILE_ARG = "out";
/// <summary>
/// Message for command-line usage
/// </summary>
private const string USAGE = "Usage: " +
"ReportingAPI --" + EMAIL_ARG + " <email> --" +
PASSWORD_ARG + " <password> [ --" +
DOMAIN_ARG + " <domain> ] --" +
REPORT_ARG + " <report name> [ --" +
DATE_ARG + " <YYYY-MM-DD> ] [ --" +
OUT_FILE_ARG + " <file name> ]";
/// <summary>
/// List of command-line arguments
/// </summary>
private static string[] PROPERTY_NAMES = new String[] {EMAIL_ARG,
PASSWORD_ARG, DOMAIN_ARG, REPORT_ARG, DATE_ARG, OUT_FILE_ARG};
/// <summary>
/// List of required command-line arguments
/// </summary>
private static string[] REQUIRED_PROPERTY_NAMES = new String[] {
EMAIL_ARG, PASSWORD_ARG, REPORT_ARG};
/// <summary>
/// Google Apps Domain
/// </summary>
public string domain = null;
/// <summary>
/// Email address of an Administrator account
/// </summary>
public string email = null;
/// <summary>
/// Password of the Administrator account
/// </summary>
public string password = null;
/// <summary>
/// Identifies the type of account
/// </summary>
private string accountType = "HOSTED";
/// <summary>
/// Identifies the Google service
/// </summary>
private string service = "apps";
/// <summary>
/// Contains a token value that Google uses to authorize
/// access to the requested report data
/// </summary>
private string token = null;
/// <summary>
/// Default constructor
/// </summary>
public ReportsManager(string username, string password, string domain)
{
this.email = username + "#" + domain;
this.password = password;
this.domain = domain;
}
/// <summary>
/// Retrieves the Authentication Token
/// </summary>
/// <returns>Returns the authentication token.</returns>
public string GetToken()
{
return this.token;
}
/// <summary>
/// Logs in the user and initializes the Token
/// </summary>
public void ClientLogin()
{
string token = null;
UTF8Encoding encoding = new UTF8Encoding();
string postData = "Email=" + System.Web.HttpUtility.UrlEncode(this.email) +
"&Passwd=" + System.Web.HttpUtility.UrlEncode(this.password) +
"&accountType=" + System.Web.HttpUtility.UrlEncode(this.accountType) +
"&service=" + System.Web.HttpUtility.UrlEncode(this.service);
byte[] data = encoding.GetBytes(postData);
HttpWebRequest request =
(HttpWebRequest)WebRequest.Create(CLIENT_LOGIN_URL);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
Stream inputStream = request.GetRequestStream();
inputStream.Write(data, 0, data.Length);
inputStream.Close();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string responseStr = (new StreamReader(
response.GetResponseStream())).ReadToEnd();
char[] tokenizer = { '\r', '\n' };
string[] parts = responseStr.Split(tokenizer);
foreach (string part in parts)
{
if (part.StartsWith("SID="))
{
token = part.Substring(4);
break;
}
}
this.token = token;
}
/// <summary>
/// Creates a XML request for the Report
/// </summary>
/// <param name="reportName">The name of the Report: activity,
/// disk_space, email_clients, quota_limit_accounts,
/// summary, suspended_account</param>
/// <param name="date">Date of the Report</param>
/// <returns>Thx XML request as a string</returns>
public string createRequestXML(string reportName, string date)
{
if (this.domain == null)
{
this.domain = getAdminEmailDomain();
}
string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
xml += "<rest xmlns=\"google:accounts:rest:protocol\"";
xml += " xmlns:xsi=\"";
xml += "http://www.w3.org/2001/XMLSchema-instance\">";
xml += "<type>Report</type>";
xml += "<token>" + this.GetToken() + "</token>";
xml += "<domain>" + this.domain + "</domain>";
xml += "<date>" + date + "</date>";
xml += "<reportType>daily</reportType>";
xml += "<reportName>" + reportName + "</reportName>";
xml += "</rest>";
return xml;
}
/// <summary>
/// Get the domain of the admin's email address.
/// </summary>
/// <returns>the domain, otherwise returns null</returns>
public string getAdminEmailDomain()
{
if (this.email != null)
{
int atIndex = this.email.IndexOf('#');
if (atIndex > 0 && atIndex + 1 < this.email.Length)
{
return this.email.Substring(atIndex + 1);
}
}
else
{
throw new ArgumentNullException("Invalid Email");
}
return null;
}
public enum ReportType
{
accounts,
activity,
disk_space,
email_clients,
quota_limit_accounts,
summary,
suspended_account
}
/// <summary>
/// Get the reports by reportName for a Date, and writes the report
/// at filename /// or to the console if filename is null.
/// </summary>
/// <param name="reportName">The name of the Report: activity,
/// disk_space, email_clients, quota_limit_accounts,summary,
/// suspended_account</param>
/// <param name="date">Date of the Report,
/// null date gets latest date available</param>
private Dictionary<string, ArrayList> getReport(string reportName, string date)
{
if (date == null)
{
date = getLatestReportDate().ToString(DATE_FORMAT);
}
else
{
try
{
date = System.Convert.ToDateTime(date).ToString
(DATE_FORMAT);
}
catch
{
throw new ArgumentException("Invalid Date");
}
}
string xml = createRequestXML(reportName, date);
HttpWebRequest request =
(HttpWebRequest)WebRequest.Create(REPORTING_URL);
request.Method = "POST";
UTF8Encoding encoding = new UTF8Encoding();
byte[] postBuffer = encoding.GetBytes(xml);
request.ContentLength = postBuffer.Length;
request.ContentType = "application/x-www-form-urlencoded";
Stream requestStream = request.GetRequestStream();
requestStream.Write(postBuffer, 0, postBuffer.Length);
requestStream.Close();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(
response.GetResponseStream());
String firstLine = null;
String lineBuffer = String.Empty;
if (reader.Peek() >= 0)
{
firstLine = reader.ReadLine();
checkError(firstLine, reader);
}
Dictionary<string, ArrayList> csv = new Dictionary<string, ArrayList>();
string[] headers = null;
if (firstLine != null)
{
headers = firstLine.Split(',');
foreach (string header in headers)
{
csv.Add(header, new ArrayList());
}
}
if (headers != null)
{
while ((lineBuffer = reader.ReadLine()) != null)
{
string[] dataLine = lineBuffer.Split(',');
for (int i = 0; i < csv.Keys.Count; i++)
{
csv[headers[i]].Add(dataLine[i]);
}
}
}
reader.Close();
return csv;
}
/// <summary>
/// Get the reports by reportName for a Date, and writes the report
/// at filename or to the console if filename is null.
/// </summary>
/// <param name="reportName">The name of the Report: activity,
/// disk_space, email_clients, quota_limit_accounts,summary,
/// suspended_account</param>
/// <param name="date">
/// Date of the Report, null date gets latest date available</param>
public Dictionary<string, ArrayList> getReport(ReportType reportType, DateTime date)
{
string reportName = Enum.GetName(typeof(ReportType), reportType);
return this.getReport(reportName, date.ToString(DATE_FORMAT));
}
/// <summary>
/// Checks for errors on the Http Response, errors are on XML format.
/// When the response is xml throws an Exception with the xml text.
/// </summary>
/// <param name="firstLine">
/// First line of the StreamReader from the Http Response</param>
/// <param name="reader">StreamReader from the Http Response</param>
private void checkError(string firstLine, StreamReader reader)
{
if (firstLine.Trim().StartsWith("<?xml"))
{
String xmlText = firstLine;
while (reader.Peek() >= 0)
{
xmlText += reader.ReadLine();
}
throw new Exception(xmlText);
}
}
/// <summary>
/// Get latest available report date,
/// based on report service time zone.
/// Reports for the current date are available after 12:00 PST8PDT
/// the following day.
/// </summary>
/// <returns>Lastest date available</returns>
public DateTime getLatestReportDate()
{
if (DateTime.UtcNow.AddHours(PUBLISH_TIME_DIFERENCE_TO_UTC).Hour
< PUBLISH_HOUR_OF_DAY)
{
return DateTime.Now.AddDays(-2);
}
else
{
return DateTime.Now.AddDays(-1);
}
}
/// <summary>
/// Gets the properties from the command-line arguments.
/// </summary>
/// <param name="args">command-line arguments</param>
/// <returns>Properties Hashtable</returns>
private static Hashtable getProperties(string[] args)
{
Hashtable properties = new Hashtable();
for (int i = 0; i < args.Length; i++)
{
bool found = false;
for (int j = 0; j < PROPERTY_NAMES.Length; j++)
{
if (args[i].Equals("--" + PROPERTY_NAMES[j]))
{
found = true;
if (i + 1 < args.Length)
{
properties.Add(PROPERTY_NAMES[j], args[i + 1]);
i++;
break;
}
else
{
throw new ArgumentException("Missing value for " +
"command-line parameter " + args[i]);
}
}
}
if (!found)
{
throw new ArgumentException(
"Unrecognized parameter " + args[i]);
}
}
for (int i = 0; i < REQUIRED_PROPERTY_NAMES.Length; i++)
{
if (properties[REQUIRED_PROPERTY_NAMES[i]] == null)
{
throw new ArgumentException("Missing value for " +
"command-line parameter " + REQUIRED_PROPERTY_NAMES[i]);
}
}
return properties;
}
}
To use it:
ReportsManager reports = new ReportsManager("*", "*", "*");
reports.ClientLogin();
Dictionary<string, ArrayList> accountReport = reports.getReport(ReportsManager.ReportType.accounts, DateTime.Today);
int count = accountReport["account_name"].Count;
Hashtable usersLastLoggedIn = new Hashtable();
for (int i = 0; i < count; i++)
{
DateTime lastLogged = DateTime.Parse(accountReport["last_login_time"][i].ToString());
DateTime lastWebMail = DateTime.Parse(accountReport["last_web_mail_time"][i].ToString());
DateTime lastPop = DateTime.Parse(accountReport["last_pop_time"][i].ToString());
if (lastWebMail > lastLogged) { lastLogged = lastWebMail; }
if (lastPop > lastLogged) { lastLogged = lastPop; }
usersLastLoggedIn.Add(accountReport["account_name"][i].ToString().Replace('#' + ConfigurationManager.AppSettings["domain"], string.Empty), lastLogged);
}