In my company there is a console app running on a windows environment serve/pc. My problem is when this Server shuts down or restarted by other people this app will be closed and have to restart the app manually for this I have to issue commands on it to start running.
and another problem is I would not know if the server state just restarted or shuts down.
I have this idea that i will build an app that would send me a sms message to my phone and alert me that this server is down or just restarted in .net vb/c#. honestly, I don't know where to start I tried to search it on the internet but found nothing. If you can help me where to start I'll appreciate it much and i will post here the development stage of this app.
thanks.
Sorry for the delay on an answer. Anyway, I have found out that there is no way to differentiate between a system shut down and a system restart. But in any case, I think your best approach is to the use the SystemEvents.SessionEnding and/or SystemEvents.SessionEnded events to capture the system/server's shutdown. The easiest way to do this would be to use a Windows Service and register these events, like so:
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
/* Choose one or both of these to register for */
SystemEvents.SessionEnding += OnSessionEnding; // Register with session ending event
SystemEvents.SessionEnded += OnSessionEnded; // Register with session ended event
}
protected override void OnStop()
{
/* Static events, so MUST deregister from them */
SystemEvents.SessionEnding -= OnSessionEnding;
SystemEvents.SessionEnded -= OnSessionEnded;
}
protected static void OnSessionEnding(Object sender, SessionEndingEventArgs e)
{
/* I suggest using SchwabenCode.EasySmtp as it is very easy to use and implements the IDisposable interface. If that is not an option, than simply use SmtpClient class */
if (e.Reason == SessionEndReasons.SystemShutdown)
{
// Send SMS message to yourself notifying shutdown is occurring on server
}
}
protected static void OnSessionEnded(Object sender, SessionEndedEventArgs e)
{
/* I suggest using SchwabenCode.EasySmtp as it is very easy to use and implements the IDisposable interface. If that is not an option, than simply use SmtpClient class */
if (e.Reason == SessionEndReasons.SystemShutdown)
{
// Send SMS message to yourself notifying shutdown is occurring on server
}
}
}
I hope that helps you get things started! Here is a enum and its extensions that I have used in the past for sending SMS messages:
/// <summary> Values that represent various carriers. </summary>
[Serializable]
public enum Carrier
{
None = 0,
Alltel = 1,
Att = 2,
BoostMobile = 3,
Sprint = 4,
Tmobile = 5,
UsCellular = 6,
Verizon = 7,
VirginMobile = 8
}
/// <summary> Carrier extensions. </summary>
public static class CarrierExtensions
{
/// <summary> Gets the email to SMS gateway for the specified carrier. </summary>
/// <param name="carrier"> The carrier to get the gateway for.</param>
/// <returns> The email to SMS gateway. </returns>
public static String GetGateway(this Carrier carrier)
{
switch (carrier)
{
case Carrier.Alltel:
return "#message.alltel.com";
case Carrier.Att:
return "#txt.att.net";
case Carrier.BoostMobile:
return "#myboostmobile.com";
case Carrier.Sprint:
return "#messaging.sprintpcs.com";
case Carrier.Tmobile:
return "#tmomail.net";
case Carrier.UsCellular:
return "#email.uscc.net";
case Carrier.Verizon:
return "#vtext.com";
case Carrier.VirginMobile:
return "#vmobl.com";
}
return String.Empty;
}
/// <summary> Formats the phone number with the appropriate email to SMS gateway. </summary>
/// <param name="carrier"> The carrier to get the gateway for.</param>
/// <param name="phoneNumber"> The phone number.</param>
/// <returns> The formatted phone number. </returns>
public static String FormatPhoneNumber(this Carrier carrier, String phoneNumber)
{
return String.Format("{0}{1}", phoneNumber, carrier.GetGateway());
}
}
Easiest would be to place the app in the startup folder:
for individual users: C:\Users[username]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
for all users: C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
But a better solution is to use the windows Task Scheduler and create a task to run the application on startup. Here is a link to an example using the scheduler.
Related
Have a WPF client that is using the latest CefSharp package to host web applications. Since we have multiple web apps we have multiple Views each with its own instance of a browser/BrowserSubProcess.
Say, for lack of a better example, I simply go into task manager and Kill one of the SubProcess.exe's. Is there an event we can tap into or otherwise be notified?
One thought would be to hook into the process by querying via some kind of pinvoke but that is a can of worms I would rather not open.
Thanks to #amaitland for pointing me in the right direction. Its a bit of a needle in a haystack but it is there.
For anyone interested, you have to implement IRequestHandler that is referenced in his comment above. You can either
do it from scratch,
use their fully implemented example at Example RequestHandler,
or do something in between using DefaultRequestHandler (DefaultRequestHandler Override Example).
So if we use DefaultRequestHandler we can do something like this for just the terminated event:
/// <summary>
/// Handle events related to browser requests.
/// </summary>
public class RequestHandler : DefaultRequestHandler
{
/// <summary>
/// Called when the render process terminates unexpectedly.
/// </summary>
/// <param name="browserControl">The ChromiumWebBrowser control</param>
/// <param name="browser">the browser object</param>
/// <param name="status">indicates how the process terminated.</param>
/// <remarks>
/// Remember that <see cref="browserControl"/> is likely on a different thread so care should be used
/// when accessing its properties.
/// </remarks>
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status)
{
switch (status)
{
case CefTerminationStatus.AbnormalTermination:
Log.Error("Browser terminated abnormally.");
break;
case CefTerminationStatus.ProcessWasKilled:
Log.Error("Browser was killed.");
break;
case CefTerminationStatus.ProcessCrashed:
Log.Error("Browser crashed while.");
break;
default:
Log.Error($"Browser terminated with unhandled status '{status}' while at address.");
break;
}
RenderProcessTerminated?.Invoke(browserControl, status);
}
/// <summary>
/// Fires when the render process terminates unexpectedly.
/// </summary>
public event EventHandler<CefTerminationStatus> RenderProcessTerminated;
}
If we have a browser object declared in the View like say:
<!--Bound to the ViewModel.Address property-->
<cef:ChromiumWebBrowser
x:Name="Browser"
Address="{Binding Address}">
</cef:ChromiumWebBrowser>
Then just wire in a new instance:
private readonly Dispatcher _mainDispatcher;
private readonly RequestHandler _requestHandler = new RequestHandler();
public MainWindow()
{
InitializeComponent();
_mainDispatcher = Dispatcher.CurrentDispatcher;
_requestHandler.RenderProcessTerminated += OnBrowserRenderProcessTerminated;
Browser.RequestHandler = _requestHandler;
}
private void OnBrowserRenderProcessTerminated(object sender, CefTerminationStatus e)
{
//Likely coming from a background thread
_mainDispatcher.InvokeAsync(() =>
Log.Error($"Browser crashed while at address: {Browser.Address}")
);
}
I am required to create separate windows service accounts for each
environment (dev, acceptance, and production) that my desktop
application uses to connect to one of our internal databases.
A global group has been added to these accounts to provide access
thereby requiring access by windows authentication using impersonation.
The connection string data is encrypted and stored
on a network, accessed by class library for security.
If I don't impersonate, and use the base constructor for the DbContext base class that accepts a connection string, it works because my personal account is assigned to the same global group. But when I encapsulate the instantiation of the DbContext object to impersonate, it fails with an internal exception stating catastrophic failure while the outer exception states
The provider did not return a ProviderManifest instance.
For example:
Console.WriteLine(Environment.UserName); //This shows me! So no impersonation yet!
using (new Impersonator("AppUser", "mydomain", "notapassword"))
{
Console.WriteLine(Environment.UserName); //This shows as AppUSER! So it works!
using (BillMarkContext dbContext = new BillMarkContext())
{
//Read each bill mark object
foreach (BillMarkCode code in dbContext.BillMarkCodes.AsEnumerable<BillMarkCode>())
Console.WriteLine(code.Code);
}
}
public partial class BillMarkContext : DbContext
{
private static string _connection = "Integrated Security=True;Persist Security Info=True;Initial Catalog=MyDB;Data Source=DBServer";
public BillMarkContext()
: base(_connection)
{}
public virtual DbSet<BillMarkCode> BillMarkCodes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{}
}
Then I tried to hard-code the connection information by creating my own DbConfiguration object, but that results in an error where it's evidently trying to do more than establish a readable connection. It's trying to create the database instead to which I do not have the rights.
Example:
[DbConfigurationType(typeof(MyDbConfiguration))]
public partial class BillMarkContext : DbContext
{
public BillMarkContext()
{}
public virtual DbSet<BillMarkCode> BillMarkCodes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{}
}
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetProviderServices("System.Data.SqlClient", SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory("Integrated Security=True;Persist Security Info=True;Initial Catalog=MyDB;Data Source=DBServer"));
}
}
This is Code-First and I can only find very simple statements and super-high level examples using DbConfiguration. And with regards to a runtime definition of connection/provider information, info always seems to be directed at a model-based approach or neglects the provider all the way around.
How do I programmatically configure EF Code-First approach to accessing a database while impersonating an application's windows service account and not get these errors?
So, I think I have finally got this worked out. It's been quite an adventure through and around many rabbit holes reading a ton of MSDN articles and many individual blogs. And in the process of working this out, I've also discovered issues in how our DB access roles were layed out. This part only goes to show that it's equally important to make sure and verify that your roles are setup correctly. Don't just take their word for it. We've got them fixed now, but probably have more controls to put in place and make sure they remain managed in the most appropriate manner. I wanted to provide my example to help someone else in the same situation as well as offer something for the veterans to comment on if they see some improvement opportunities. The sample below is still a bit naive in the since that I could probably do more around SecureString, but I figure that's not the core of the issue. So, here goes...
Consumer:
[STAThread]
static void Main(string[] args)
{
try
{
string domain = "myDomain";
string userName = "myUserName";
string password = "NotAPassword"
//Using NEW_CREDENTIALS is the same as RunAs with the /netonly switch set. Local computer login is based on the
//current user. While the impersonated account will be used for remote access to resources on the network.
//Therefore authentication across the domain.
//Per MSDN, NEW_CREDENTIALS should only work with the WINNT50 provider type. However, I have verified this to work with Default.
//I'm just not sure of the long-term implications since MS made a point to specify this.
using (Impersonator.LogonUser(domain, userName, password, LogonType.NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
{
//This will show the currently logged on user (machine), because NEW_CREDENTIALS doesn't alter this, only remote access
Console.WriteLine("Current user...{0}", WindowsIdentity.GetCurrent().Name);
using (BillMarkContext dbContext = new BillMarkContext())
{
//Read each bill mark object
foreach (BillMarkCode code in dbContext.BillMarkCodes.AsEnumerable<BillMarkCode>())
{
Console.WriteLine(code.Code);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadLine();
}
Context:
Obviously a real-world implementation will not store the connection string in a static field.
public partial class BillMarkContext : DbContext
{
private static string _connection4 = "Integrated Security=True;Persist Security Info=True;Initial Catalog=MyDB;Data Source=MyServer";
public BillMarkContext()
: base(_connection4)
{
//Since we're read-only
Database.SetInitializer<BillMarkContext>(null);
}
//View property setup since we're read-only
protected virtual DbSet<BillMarkCode> _billMarkCodes { get; set; }
public DbQuery<BillMarkCode> BillMarkCodes
{
get { return Set<BillMarkCode>().AsNoTracking(); }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ }
}
Impersonator & supporting classes/enums:
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
internal sealed class Impersonator : IDisposable
{
#region Properties
private SafeTokenHandle _handle;
private WindowsImpersonationContext _context;
private bool _isDisposed;
public bool IsDisposed
{
get { return _isDisposed; }
private set { _isDisposed = value; }
}
#endregion
#region Constructors / Factory Methods
private Impersonator(string domain, string userName, string password, LogonType logonType, LogonProvider provider)
{
bool gotTokenHandle = NativeLoginMethods.LogonUser(userName, domain, password, (int)logonType, (int)provider, out _handle);
if (!gotTokenHandle || _handle.IsInvalid)
{
int errorCode = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(errorCode);
}
}
public static Impersonator LogonUser(string domain, string userName, string password, LogonType logonType, LogonProvider provider)
{
Impersonator impersonator = new Impersonator(domain, userName, password, logonType, provider);
impersonator._context = WindowsIdentity.Impersonate(impersonator._handle.DangerousGetHandle());
return impersonator;
}
#endregion
#region Dispose Pattern Methods
private void Dispose(bool disposing)
{
//Allow the Dispose() to be called more than once
if (this.IsDisposed)
return;
if (disposing)
{
// Cleanup managed wrappers
if (_context != null)
_context.Dispose();
if (_handle != null && !_handle.IsClosed)
_handle.Dispose();
//Suppress future calls if successful
this.IsDisposed = true;
}
}
public void Dispose()
{
//Dispose the resource
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
internal class NativeLoginMethods
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(IntPtr handle);
}
internal sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
#region Constructors
internal SafeTokenHandle()
: base(true)
{ }
#endregion
#region Support Methods
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
protected override bool ReleaseHandle()
{
return NativeLoginMethods.CloseHandle(base.handle);
}
#endregion
}
/// <summary>
/// Logon Type enum
/// </summary>
internal enum LogonType : int
{
/// <summary>
/// This logon type is intended for users who will be interactively using the computer, such as a user being logged on by a terminal server, remote shell, or similar process. This logon type has the additional expense of caching logon information for disconnected operations; therefore, it is inappropriate for some client/server applications, such as a mail server.
/// </summary>
INTERACTIVE = 2,
/// <summary>
/// This logon type is intended for high performance servers to authenticate plaintext passwords. The LogonUser function does not cache credentials for this logon type.
/// </summary>
NETWORK = 3,
/// <summary>
/// This logon type is intended for batch servers, where processes may be executing on behalf of a user without their direct intervention. This type is also for higher performance servers that process many plaintext authentication attempts at a time, such as mail or web servers.
/// </summary>
BATCH = 4,
/// <summary>
/// Indicates a service-type logon. The account provided must have the service privilege enabled.
/// </summary>
SERVICE = 5,
/// <summary>
/// GINAs are no longer supported. Windows Server 2003 and Windows XP: This logon type is for GINA DLLs that log on users who will be interactively using the computer. This logon type can generate a unique audit record that shows when the workstation was unlocked.
/// </summary>
UNLOCK = 7,
/// <summary>
/// This logon type preserves the name and password in the authentication package, which allows the server to make connections to other network servers while impersonating the client. A server can accept plaintext credentials from a client, call LogonUser, verify that the user can access the system across the network, and still communicate with other servers.
/// </summary>
NETWORK_CLEARTEXT = 8,
/// <summary>
/// This logon type allows the caller to clone its current token and specify new credentials for outbound connections. The new logon session has the same local identifier but uses different credentials for other network connections. This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
/// </summary>
NEW_CREDENTIALS = 9
}
internal enum LogonProvider : int
{
/// <summary>
/// Use the standard logon provider for the system. The default security provider is negotiate, unless you pass NULL for the domain name and the user name is not in UPN format. In this case, the default provider is NTLM.
/// </summary>
LOGON32_PROVIDER_DEFAULT = 0,
/// <summary>
/// Use the Windows NT 3.5 logon provider.
/// </summary>
LOGON32_PROVIDER_WINNT35 = 1,
/// <summary>
/// Use the NTLM logon provider.
/// </summary>
LOGON32_PROVIDER_WINNT40 = 2,
/// <summary>
/// Use the negotiate logon provider.
/// </summary>
LOGON32_PROVIDER_WINNT50 = 3
}
I have a somewhat simple web app, that uses an ASMX web service as its sole data access. All the information is gotten from it, and saved to it. It works fine so thats out of the way.
I just updated to VS2012, and it complained about the class implementing the service reference, does not inherit from IDisposeable.
After some reading, i am more confused as some solutions are really elaborate, some are simple. Short version is, after understanding so little, it seems like i cant adapt it to how my app is made.
I have several data access classes, all focusing on methods for an area. For example, one dataaccess for customer related calls, one for product related calls etc.
But since they are all using the same service, they all derive from a base data access class that holds the reference.
This is the base data access class:
public class BaseDataAccess
{
private dk.odknet.webudv.WebService1 _service;
private string _systemBrugerID, _systemPassword;
public BaseDataAccess()
{
//Gets the system user and password that is stored in the webconfig file. This means you only have to change
//the username and password in one place without having to change the code = its not hardcoded.
_systemBrugerID = System.Configuration.ConfigurationManager.AppSettings["SystemBrugerID"].ToString();
_systemPassword = System.Configuration.ConfigurationManager.AppSettings["SystemPassword"].ToString();
_service = new dk.odknet.webudv.WebService1();
}
/// <summary>
/// Gets an instance of the webservice.
/// </summary>
protected dk.odknet.webudv.WebService1 Service
{
get { return _service; }
}
/// <summary>
/// Gets the system user id, used for certain methods in the webservice.
/// </summary>
protected string SystemBrugerID
{
get { return _systemBrugerID; }
}
/// <summary>
/// Gets the system user password, used for certain methods in the webservice.
/// </summary>
protected string SystemPassword
{
get { return _systemPassword; }
}
}
And here is how a derived class utilizes the service reference from the base class:
public class CustomerDataAccess : BaseDataAccess
{
public CustomerDataAccess() {}
/// <summary>
/// Get's a single customer by their ID, as the type "Kunde".
/// </summary>
/// <param name="userId">The user's username.</param>
/// <param name="customerId">Customer's "fkKundeNr".</param>
/// <returns>Returns a single customer based on their ID, as the type "Kunde".</returns>
public dk.odknet.webudv.Kunde GetCustomerById(string userId, string customerId)
{
try
{
return Service.GetKunde(SystemBrugerID, SystemPassword, userId, customerId);
}
catch (Exception e)
{
Debug.WriteLine(e);
throw;
}
}}
So how on earth do i implement IDisposable in this situation? I just cant wrap my head around it.
EDIT
I have fiddled with the service reference, and come up with this:
/// <summary>
/// Gets an instance of the webservice.
/// </summary>
protected dk.odknet.webudv.WebService1 Service
{
get
{
try
{
using (_service = new dk.odknet.webudv.WebService1())
{
return _service;
}
}
catch (Exception e)
{
Debug.WriteLine(e);
throw;
}
}
}
Yes the exception handling isnt great, i will get to that (advice is appreciated), but VS2012 does not complain about the lack of IDisposable anymore.
Instantiation of the service has been removed from the constructor. The app works fine without any further modifications.
Will this suffice?
First of all I wanted to thank all of you for your continuous contributions to the Stack Overflow community! I've been a member of Stack Overflow for years and have come to rely on your input more so than any other source online. Though I try to participate and answer members' questions whenever I can, every once in a while I find myself stuck and in need of help.
Speaking of which I have an unusual code problem. I am writing an API library in C# that needs to be able to be called from WPF/Windows Forms application, but also from within Unit Test code.
The issue is that I need to be able to report (in Excel) on whether each method of the library executed properly when the API is called from within a WPF/windows forms application, along some other metadata and optionally a return type.
When the code is consumed within Unit Tests I don't really care about the reporting, but I do need to be able to produce an Assert on whether the API call executed properly or not.
For instance, if in a Unit Test we have an Test Initialize portion, one of the API calls may be to create a Domain User for the test method to use. Another one may also create a Domain Group, so that the user has proper group membership.
To accomodate the consumption of the API from WPF/WinForms, I've been rewriting every function in the API to return a OperationStep type, with the hopes that when all API calls have executed I would have an IEnumerable<OperationStep> which I can write to a CSV file.
So the question is is there an easier way of achieving what I have done so far? The reporting is extremely tedious and time consuming to code, considering that the API library consists of hundreds of similar methods. Samples are described bellow:
OperationStep<PrincipalContext> createDomainConnectionStep = DomainContext.Current.GetPrincipalContext(settings.DomainInfo);
OperationStep<UserPrincipal> createDomainUserStep = DomainContext.Current.CreateUser(createDomainConnectionStep.Context, settings.TestAccountInfo.Username, settings.TestAccountInfo.Password);
OperationStep<GroupPrincipal> createDomainGroupStep = DomainContext.Current.CreateGroup(createDomainConnectionStep.Context, settings.TestAccountInfo.UserGrupName);
Where the DomainContext is a singleton object whose functionality is to connect to the domain controller and create a user, group, and associate the user to a group.
Note that both the second and the third method call require the output of the first, and therefore warranting the need for having the public T Context within the OperationResult object as described bellow.
The OperationStep object consists of the following properties which are inherited by the IOperation interface with the exception of the public T Context.
public class OperationStep<T> : IOperation
{
/// <summary>
/// Denotes the Logical Name of the current operation
/// </summary>
public string Name { get; set; }
/// <summary>
/// Denotes the stage of execution of the current operation: Setup, Execution, Validation, Cleanup
/// </summary>
public OperationStage Stage { get; set; }
/// <summary>
/// Denotes whether the test step completed properly or failed.
/// </summary>
public OperationResult Result { get; set; }
/// <summary>
/// Denotes the return type of the test method.
/// </summary>
public T Context { get; set; }
/// <summary>
/// Denotes any other relevant information about the test step
/// </summary>
public string Description { get; set; }
/// <summary>
/// If the test step result is failed, this should have the stack trace and the error message.
/// </summary>
public string Error { get; set; }
}
The method calls themselves are a bit bloated and tedious but here is a sample.
public class DomainContext
{
private static volatile DomainContext currentContext;
private static object synchronizationToken = new object();
/// <summary>
/// default ctor.
/// </summary>
private DomainContext() { }
/// <summary>
/// Retrieves the Current DomainContext instance.
/// </summary>
public static DomainContext Current
{
get
{
if (currentContext == null)
{
lock (synchronizationToken)
{
if (currentContext == null)
{
currentContext = new DomainContext();
}
}
}
return currentContext;
}
}
/// <summary>
/// Establishes a connection to the domain.
/// </summary>
/// <param name="domainInfo"></param>
/// <returns></returns>
public OperationStep<PrincipalContext> GetPrincipalContext(DomainInfo domainInfo)
{
OperationStep<PrincipalContext> result = new OperationStep<PrincipalContext>();
result.Name = "Establish Connection to Active Directory";
result.Result = OperationResult.Success;
result.Stage = OperationStage.Setup;
result.Description = string.Format("Domain Name: {0}, Default Containter: {1}", domainInfo.FQDN, domainInfo.Container);
try
{
ContextType contextType = this.GetContextType(domainInfo.DomainType);
PrincipalContext principalContext;
try
{
principalContext = new PrincipalContext(contextType, domainInfo.FQDN, domainInfo.Container);
}
catch
{
throw new Exception("Unable to establish connection to Active Directory with the specified connection options.");
}
if (principalContext != null)
{
bool authenticationResult = principalContext.ValidateCredentials(domainInfo.Username, domainInfo.Password);
if (!authenticationResult)
{
throw new Exception("Unable to authenticate domain admin user to Active Directory.");
}
result.Context = principalContext;
result.Result = OperationResult.Success;
}
}
catch(Exception ex)
{
result.Error = ex.Message;
result.Result = OperationResult.Failure;
}
return result;
}
}
When all method calls have executed theoreticaly I should have an IEnumerable<IOperation> which in the case of a win form I can write in a csv file (to be viewed in MS Excel) or in the case of a unit test I can simply omit the extra info and ignore (other than the method executed successively and the T Context property).
If I understood you correctly - all that OperationSteps are here only for logging. Then why not enable simple .NET logging? Log needed info where it is convenient for you. You can use TraceSource with DelimetedTraceListener to write to .csv file. More than that. You can move logging logic to Strategy class and override its logging methods in your unit test so that instead of logging you call Assert methods.
I am using the dotnetopenauth library and I am trying to figure out how to change the call back url.
I am looking at the sample file
public partial class SignInWithTwitter : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
if (TwitterConsumer.IsTwitterConsumerConfigured) {
this.MultiView1.ActiveViewIndex = 1;
if (!IsPostBack) {
string screenName;
int userId;
if (TwitterConsumer.TryFinishSignInWithTwitter(out screenName, out userId)) {
this.loggedInPanel.Visible = true;
this.loggedInName.Text = screenName;
// In a real app, the Twitter username would likely be used
// to log the user into the application.
////FormsAuthentication.RedirectFromLoginPage(screenName, false);
}
}
}
}
protected void signInButton_Click(object sender, ImageClickEventArgs e) {
TwitterConsumer.StartSignInWithTwitter(this.forceLoginCheckbox.Checked).Send();
}
So this is all what is needed to send a request off to twitter( for webforms slightly different for mvc).
I would like to change the url that the response comes back too(I rather have a separate action result.
Now it seems like StartSignInWithTwitter() is where it sets the url.
/// <summary>
/// Prepares a redirect that will send the user to Twitter to sign in.
/// </summary>
/// <param name="forceNewLogin">if set to <c>true</c> the user will be required to re-enter their Twitter credentials even if already logged in to Twitter.</param>
/// <returns>The redirect message.</returns>
/// <remarks>
/// Call <see cref="OutgoingWebResponse.Send"/> or
/// <c>return StartSignInWithTwitter().<see cref="MessagingUtilities.AsActionResult">AsActionResult()</see></c>
/// to actually perform the redirect.
/// </remarks>
public static OutgoingWebResponse StartSignInWithTwitter(bool forceNewLogin) {
var redirectParameters = new Dictionary<string, string>();
if (forceNewLogin) {
redirectParameters["force_login"] = "true";
}
Uri callback = MessagingUtilities.GetRequestUrlFromContext().StripQueryArgumentsWithPrefix("oauth_");
var request = TwitterSignIn.PrepareRequestUserAuthorization(callback, null, redirectParameters);
return TwitterSignIn.Channel.PrepareResponse(request);
}
It seems to be hard coded to get the current request from context. is it possible to override this somehow without me actually changing this line of code and recompiling the .dll?
Edit
For now I made changes to the .dll - I really don't like this way as now i got to support a custom version.
public static OutgoingWebResponse StartSignInWithTwitter(bool forceNewLogin) {
Uri callback = MessagingUtilities.GetRequestUrlFromContext().StripQueryArgumentsWithPrefix("oauth_");
return StartProcess(forceNewLogin, callback);
}
private static OutgoingWebResponse StartProcess(bool forceNewLogin, Uri callback)
{
var redirectParameters = new Dictionary<string, string>();
if (forceNewLogin)
{
redirectParameters["force_login"] = "true";
}
var request = TwitterSignIn.PrepareRequestUserAuthorization(callback, null, redirectParameters);
return TwitterSignIn.Channel.PrepareResponse(request);
}
public static OutgoingWebResponse StartSignInWithTwitter(bool forceNewLogin, Uri callback)
{
return StartProcess(forceNewLogin, callback);
}
Hopefully there is another way.
I appreciate your desire to not recompile libraries. But the DotNetOpenAuth.ApplicationBlock library is just an auxiliary library to DotNetOpenAuth and it ships with source because it isn't really intended to be used as-is. It's expected that you'll copy and paste the source code out of it into your web application that you need and dump the rest. For example, there is no backward compatibility promise in the applicationblock library from version to version like there is in the core dotnetopenauth.dll file.
So by all means, if you find a deficiency in the appblock, you're in the right to fix it for yourself.