How to get job's information (name, status, last run, etc) in SQL Server agent using c# and mvc? - asp.net

I am a rookie with ASP.NET currently developing monitoring system for SQL Server, and still working on retrieving job's information so I can show them on the page.
I tried to use Mr. Alexey Zimarev's code this code
And I wrote it on my controller like this
public class JobsController : Controller
{
static readonly string SqlServer = #"DESKTOP-PQD9KKN";
private ApplicationDbContext _context;
public JobsController()
{
_context = new ApplicationDbContext();
}
protected override void Dispose(bool disposing)
{
_context.Dispose();
}
[Route("Customers/MigrateJob")]
public ActionResult MigrateJob()
{
ServerConnection conn = new ServerConnection(SqlServer);
Server server = new Server(conn);
JobCollection jobs = server.JobServer.Jobs;
foreach(Job job in jobs)
{
var jobactivity = new JobActivity
{
Name = job.Name,
IsEnabled = job.IsEnabled
....so on
};
_context.JobActivities.Add(jobactivity);
}
return RedirectToAction("List", "Jobs");
}
public ActionResult List()
{
var jobactivities = _context.JobActivities.ToList();
return View(jobactivities);
}
}
My approach is to store the job's information from SQL Server Agent to my JobActivity table using MigrateJob(). The problem is the job's information hasn't stored in my table yet without any error messages.
My Tools:
VS 2017
SQL Server ver 13
Any help would be appreciated :)

Related

Entity Framework tables are not generated using in memory SQLite

We are trying to move to using an in-memory SQLite instance for our unit test automation, instead of SQL Server or SQL Express. We use Entity Framework Core.
I think I have everything configured correctly, but it's still failing, so I must be missing a step, but I'm not sure what it is.
In our test project's app.config, I've specified:
<connectionStrings>
<add name="BusinessDb" providerName="System.Data.SQLite.EF6" connectionString="data source=:memory:"/>
</connectionStrings>
Our production concrete class is a bit more complex (it has many more modelBuilder calls in the OnModelCreating() method and many more DbSet objects, but it is basically like this:
namespace Business.Base.Concrete
{
public class SqlBusinessDb
: DbContext
, IBusinessDb
{
public string ConnectionString { get; set; }
public SqlBusinessDb(string connectionString)
{
ConnectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
if (ConnectionString.Contains("memory"))
{
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlite(ConnectionString,
options =>
options.CommandTimeout(SqlSettings.s_CommandTimeoutInSec.CurrentValue)
.MigrationsHistoryTable("_BusinessDB_Migrations"))
.AddInterceptors(new Deals.Base.SQL.SqlPerfCounterInterceptor());
}
else
{
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer(ConnectionString,
options =>
options.CommandTimeout(SqlSettings.s_CommandTimeoutInSec.CurrentValue)
.MigrationsHistoryTable("_BusinessDB_Migrations")
.EnableRetryOnFailure())
.AddInterceptors(new Deals.Base.SQL.SqlPerfCounterInterceptor());
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Has<BillingPlan>()
.HasManyToOne(p => p.Companies, a => a.BillingPlan, a => a.BillingPlan_Id)
}
public int ExecuteStoreCommand(string commandText, params object[] parameters)
{
return Database.ExecuteSqlRaw(commandText, parameters);
}
public DbSet<Features.FeatureOverride_Plan> FeaturesPlan { get; set; }
public DbSet<Business> Businesses { get; set; }
}
}
In our test project we call it like so:
public static TestBusinessDb GetInstance()
{
SqlBusinessDb realRepository = new SqlBusinessDb();
if (!_hasBeenMigrated)
{
_hasBeenMigrated = true;
DatabaseFacade dbf = realRepository.Database;
var issqlite = dbf.IsSqlite();
var tables = dbf.ExecuteSqlRaw("SELECT * FROM information_schema.tables;");
// for the Test Repository, we migrate once when we first try and connect.
realRepository.Database.Migrate();
}
}
This code fails on the "dbf.ExecuteSqlRaw()" line with:
Microsoft.Data.Sqlite.SqliteException : SQLite Error 1: 'no such table: information_schema.tables'.
If I remove that line, it fails on: realRepository.Database.Migrate(); with
Microsoft.Data.Sqlite.SqliteException : SQLite Error 1: 'no such table: _BusinessDB_Migrations'.
When debugging it successfully ran the OnConfiguring and OnModelCreating methods and I watched it execute a SQL command that created that table. dbf.ProviderName returns "Microsoft.EntityFrameworkCore.Sqlite". So, why aren't the tables being found? Is there something else that needs to be in place that I'm missing?
It turns out that SQLite is unable to handle migrations anyway, so it is not a viable option.

Sharing Db Connection Across POCOs in ASP.NET 5 (vNext)

I'm learning how to use ASP.NET 5 (vNext). In an attempt to do this, I'm working on a basic application. In this application, I'm trying to connect to the database from a couple of POCOs (Customer, Order, etc.) using Dapper. If I understand things correctly, its expensive to create, connect to, and tear down connections to a database. If this is true, I'm trying to figure out the recommended way to share a connection across multiple objects.
Currently, I have the following:
public class Order
{
private IDbConnection _connection;
public void Save()
{
using (_connection = new SqlConnection("[MyConnectionString]")
{
_connection.Open();
_connection.Execute("[INSERTION SQL]");
}
}
public List<Order> FindByCustomerEmailAddress(string emailAddress)
{
using (_connection = new SqlConnection("[MyConnectionString]")
{
_connection.Open();
return _connection.Query<List<Order>>("SELECT o.* FROM Order o, Customer c WHERE o.CustomerId=c.CustomerId AND c.EmailAddress='" + emailAddress + "'" );
}
}
}
public class Customer
{
private IDbConnection _connection;
public void Save()
{
using (_connection = new SqlConnection("[MyConnectionString]")
{
_connection.Open();
_connection.Execute("[INSERTION SQL]");
}
}
public Customer FindByEmailAddress(string emailAddress)
{
using (_connection = new SqlConnection("[MyConnectionString]")
{
_connection.Open();
return _connection.Query<Customer>("SELECT * FROM Customer WHERE EmailAddress='" + emailAddress + "'" );
}
}
}
I thought about creating a Database class that looks like this:
public static class Database
{
private static IDbConnection Connection { get; set; }
public static IDbConnection GetConnection()
{
if (Connection == null)
{
Connection = new SqlConnection("[MyConnectionString]");
Connection.Open();
}
return Connection;
}
}
public class Order
{
public void Save()
{
var connection = Database.GetConnection();
connection.Execute("[INSERTION SQL]");
}
public List<Order> FindByCustomerEmailAddress(string emailAddress)
{
var connection = Database.GetConnection();
return connection.Query<List<Order>>("SELECT ...");
}
}
However, after thinking about this, I'm not sure if this a good strategy for managing a database connection. The use of static in this manner seems dangerous. Yet, it seems like someone has had to solve this issue. But, nothing I've seen is explained so I do not understand if it actually works. Can someone share with me what the recommended approach for managing database connections in an efficient manner is?
Thank you!
Opening and closing connection to a database server is indeed expensive. However, .NET implements connection pooling just for this reason, and it is on by default. You can modify the setting of how many connection it should keep open (I don't recall the default).
So, if your connection string is the same, .NET will reuse an open connection from the pool and use that. If different, it'll create a new one.
Your first code is correct in using "using" so when you're done, the dispose/close will give the connection back to the pool.
See more about this here; https://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.110).aspx

how to update search results using signalR

I've just started learning signalR and I'm trying to implement a search feature.
How would i go about periodically updating a user's search result. My initial idea is to run a timed job via IRegisteredObject to trigger a check from client with search params like so:
public class BackgroundTimer : IRegisteredObject
{
private Timer taskTimer;
private IHubContext hub;
public BackgroundTimer()
{
HostingEnvironment.RegisterObject(this);
hub = GlobalHost.ConnectionManager.GetHubContext<SearchHub>();
taskTimer = new Timer(OnTimerElapsed, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5));
}
private void OnTimerElapsed(object sender)
{
hub.Clients.All.checkForUpdates();
}
}
public class SearchHub : Hub
{
public void Search(string searchText)
{
string jsonResult = string.Empty;
//TODO save result to jsonResult
Clients.Caller.broadcastMessage(jsonResult);
}
}
However i can't help but think there are much more efficient ways to accomplish this. Any advice pls
With this code you are just doing what the clients could instead, poll the server each second. Either publish a message on all actions that alter the search result and act on that. Or use SqlDependency.

using WCF Callback and asp.net to implement publish/subscribe pattern

This is my first web application with WCF. So please guide me as a new guy.
I'm trying to use WCF Callback to implement publish/subscribe pattern. I would like to send the message from UserA to UserB or UserA to every client at the moment. I got an example from here .
In my app, I use ASP.NET as a client to connect WCF service instead and I found a problem when I subscribe to WCF service.
The WCF service does not hold any other clients object. So when I call GetAllClients(_guid), it will return only 1 client which is itself.
Here is the code in ASP.NET page (I put every control inside updatePanel)
public partial class _Default : System.Web.UI.Page, AlertServiceCallback
{
private AlertServiceClient _client;
private Guid _guid = Guid.NewGuid();
protected void Page_Load(object sender, EventArgs e)
{
InstanceContext context = new InstanceContext(this);
_client = new AlertServiceClient(context);
_client.RegisterClient(_guid);
}
protected void btnGetClients_Click(object sender, EventArgs e)
{
//Try to retrive all active clients
Client[] cs = _client.GetAllClients(_guid);
List<Client> list = new List<Client>(cs);
//Bind to dropDownList to display all active clients
ddlClients.DataSource = list;
ddlClients.DataBind();
}
#region "CallBack"
public void OnRegister(string message)
{
throw new NotImplementedException();
}
public void OnMessageSending(string message)
{
throw new NotImplementedException();
}
#endregion
}
Here is the IService and Service on WCF respectively.
[ServiceContract(Name = "AlertService",Namespace = "http://www.testWcf.com/",
CallbackContract = typeof(IAlertCallBack),SessionMode = SessionMode.Required)]
public interface IAlertService
{
[OperationContract(IsOneWay = true)]
void RegisterClient(Guid id, string name);
[OperationContract(IsOneWay = false)]
List<Client> GetAllClients(Guid id);
[OperationContract(IsOneWay = true)]
void SendMessage(Guid fromId, Guid toId, string message);
}
public interface IAlertCallBack
{
[OperationContract(IsOneWay = true)]
void OnRegister(string message);
[OperationContract(IsOneWay = true)]
void OnMessageSending(string message);
}
public class AlertService : IAlertService
{
private object locker = new object();
private Dictionary<Client, IAlertCallBack> clients = new Dictionary<Client, IAlertCallBack>();
public AlertService() { }
public void RegisterClient(Guid guid)
{
IAlertCallBack callback = OperationContext.Current.GetCallbackChannel<IAlertCallBack>();
//---prevent multiple clients adding at the same time---
lock (locker)
{
clients.Add(new Client { Id = guid, Name = name }, callback);
}
}
public List<Client> GetAllClients(Guid guid)
{
//---get all the clients in dictionary---
return (from c in clients
where c.Key.Id != guid
select c.Key).ToList();
}
...
}
Questions are:
Is it possible to implement this publish/subscribe with ASP.NET and WCF callback? (I already tried on window app and it worked fine)
If it is possible, how can I keep all clients that is connecting to WCF Service so I can use those GuId to call callback method.
Thank you.
I don't know why you don't get list of clients - you should but there are much worse problems with your code.
You can't use WCF callback in ASP.NET application. It cannot work because page lives only to serve single HTTP request - it means it most probably lives only for fraction of second and so also your registration. Even If you will be able to get list of clients you will not be able to call OnRegister or OnMessageSending because there will be no proxy listening for these calls.
Even if you force proxy to live after request processing it will still notify only code behind, not your pages rendered in client browser.
Another problem is that it can work only with net.pipe or net.tcp binding. It will not work with wsDualHttp. It is very problematic to open multiple duplex clients from the same machine when using wsDualHttp.
You are doing it completely wrong. What you need is AJAX polling from client browser to asp.net which will call simple service in your chat system.

Dependency Injection Query

I'm starting a web application that contains the following projects:
Booking.Web
Booking.Services
Booking.DataObjects
Booking.Data
I'm using the repository pattern in my data project only. All services will be the same, no matter what happens. However, if a customer wants to use Access, it will use a different data repository than if the customer wants to use SQL Server.
I have StructureMap, and want to be able to do the following:
Web project is unaffected. It's a web forms application that will only know about the services project and the dataobjects project.
When a service is called, it will use StructureMap (by looking up the bootstrapper.cs file) to see which data repository to use.
An example of a services class is the error logging class:
public class ErrorLog : IErrorLog
{
ILogging logger;
public ErrorLog()
{
}
public ErrorLog(ILogging logger)
{
this.logger = logger;
}
public void AddToLog(string errorMessage)
{
try
{
AddToDatabaseLog(errorMessage);
}
catch (Exception ex)
{
AddToFileLog(ex.Message);
}
finally
{
AddToFileLog(errorMessage);
}
}
private void AddToDatabaseLog(string errorMessage)
{
ErrorObject error =
new ErrorObject
{
ErrorDateTime = DateTime.Now,
ErrorMessage = errorMessage
};
logger.Insert(error);
}
private void AddToFileLog(string errorMessage)
{
// TODO: Take this value from the web.config instead of hard coding it
TextWriter writer = new StreamWriter(#"E:\Work\Booking\Booking\Booking.Web\Logs\ErrorLog.txt", true);
writer.WriteLine(DateTime.Now.ToString() + " ---------- " + errorMessage);
writer.Close();
}
}
I want to be able to call this service from my web project, without defining which repository to use for the data access. My boostrapper.cs file in the services project is defined as:
public class Bootstrapper
{
public static void ConfigureStructureMap()
{
ObjectFactory.Initialize(x =>
{
x.AddRegistry(new ServiceRegistry());
}
);
}
public class ServiceRegistry : Registry
{
protected override void configure()
{
ForRequestedType<IErrorLog>().TheDefaultIsConcreteType<Booking.Services.Logging.ErrorLog>();
ForRequestedType<ILogging>().TheDefaultIsConcreteType<SqlServerLoggingProvider>();
}
}
}
What else do I need to get this to work? When I defined a test, the ILogger object was null.
Perhaps some details on how you are calling this code from a test would be useful.
My understanding is that you need to ensure that the ConfigureStructureMap call has been made early in the applications life (e.g. in the Global.asax in a web project).
After that you would be calling for instances of IErrorLog using something like:
IErrorLog log = StructureMap.ObjectFactory.GetNamedInstance<IErrorLog>();

Resources