how to modify a DbSet at an specific time? - asp.net-mvc

I am working on an ASP NET MVC 5 website and I want to modify an element of a DbSet only once at the start of a new day, month and year, but I can't find any example on the internet doing this, any help on how to do this?
lets say I have:
public class File
{
public int FileID { get; set; }
public int Votes { get; set; }
}
and
public DbSet<File> Files { get; set; }
and I want to change a file votes to 0 at the start of a new day only once:
var modFile = new File{ FileID = 2, Votes = 0};
db.Entry(modFile).State = EntityState.Modified;
db.SaveChanges();
Where in a MVC 5 project do I put this code?
How it gets triggered?

If you have an external Service layer (that is independent of .NET) which contains your objects (in your case, File.cs, etc..) then using the built-in Windows scheduler is fine (it triggers executable code at a certain time, as defined by the user).
To do this, you may want to create a Console Application that has a reference to the Service dll and the connection of your database.
Console Application
In Visual Studio, go to File -> New Project -> Visual C# -> Console Application.
Within the App.config file, you can add the connection string to your database. For example:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="SchoolDBConnectionString"
connectionString="Data Source=...;Initial Catalog=...;Integrated Security=true"
providerName="System.Data.SqlClient"/>
</connectionStrings>
</configuration>
You can then set up a reference to your Service.dll which should have the database context accessible, e.g. DataContext db = MyService.Data.DataContext();
So instead of:
var modFile = new File{ FileID = 2, Votes = 0};
db.Entry(modFile).State = EntityState.Modified;
db.SaveChanges();
You could use:
db.Files.Where(s => s.Votes > 0).ToList().ForEach(s => s.Votes = 0);
db.SavesChanges();
You can run the project in release mode and grab the relevant dll's and exe file. Within the Task Scheduler you are then able to create a task that runs a specific exe.
Service
Technically speaking, you don't have to have this level of isolation -- but in my opinion it's good practice. You could just create a reference to your MVC project, but I personally wouldn't.
To create a Service layer..
Right click your solution (where your MVC application is within) -> Add -> New Project -> Visual C# -> Class Library
Within this project, you should move all your objects (File.cs, etc) within here. You are then able to create a reference to this project within your MVC project by right clicking References and selecting the Service library you just created. You can do the same for the Console Application.
This will then create a layer of isolation between your MVC application and concrete (business) logic.
Otherwise, if you have to schedule your tasks within ASP.NET check out Scott Hanselman's post -- he has compiled together a list of libraries that schedule jobs at certain times. It's however important to understand that ASP.NET applications should only really deal with user requests and responses - threads are somewhat dangerous.

Related

Cannot connect to my edmx with my data context

I've not used VS MVC for a while but I'm writing a project which requires connecting to a Sql database which I've installed as an edmx file SwitchDB.edmx in my DAL folder. In the past I've set up my data context file which I then use to reference the data in my controller, the model help me to order the data in the correct way.
This is how my data context file looks
namespace Switches.DAL
{
public class SwitchContext : DbContext
{
public SwitchContext()
: base("DefaultConnection")
{ }
public DbSet<Switch_List> SwitchList { get; set; }
}
}
I've set up the "DefaultConnection" in my Web.config under connectionStrings and my model Switch_List.cs has the file settings. When I declare the DB context in my controller as below
private SwitchContext db = new SwitchContext();
Then I would expect to reference the SwitchContext to get my data, like this
var switches= db.SwitchList .ToList();
However, when I run the project and reference db in debug I get the following error message 'the function evaluation requires all threads to run'. The DB context SwitchContext is clearly not getting access to the Switch.edmx so what am I forgetting?
I had a similar problem, but you should see the connection properties using an IDE button (to re-evaluate the expression).
However, when you get to the part of db.SwitchList.ToList() does it generate any exceptions?

Hangfire job on Console/Web App solution?

I'm new to Hangfire and I'm trying to understand how this works.
So I have a MVC 5 application and a Console application in the same solution. The console application is a simple one that just updates some data on the database (originally planned to use Windows Task Scheduler).
Where exactly do I install Hangfire? In the Web app or the console? Or should I convert the console into a class on the Web app?
If I understand it correctly, the console in your solution is acting like an "pseudo" HangFire, since like you said it does some database operations overtime and you plan to execute it using the Task Scheduler.
HangFire Overview
HangFire was design to do exactly what you want with your console app, but with a lot more of power and functionalities, so you avoid all the overhead of creating all that by yourself.
HangFire Instalation
HangFire is installed commonly alongside with ASP.NET Applications, but if you carefully read the docs, you will surprisingly find this:
Hangfire project consists of a couple of NuGet packages available on
NuGet Gallery site. Here is the list of basic packages you should know
about:
Hangfire – bootstrapper package that is intended to be installed only
for ASP.NET applications that uses SQL Server as a job storage. It
simply references to Hangfire.Core, Hangfire.SqlServer and
Microsoft.Owin.Host.SystemWeb packages.
Hangfire.Core – basic package
that contains all core components of Hangfire. It can be used in any
project type, including ASP.NET application, Windows Service, Console,
any OWIN-compatible web application, Azure Worker Role, etc.
As you can see, HangFire can be used in any type of project including console applications but you will need to manage and add all the libraries depending on what kind of job storage you will use. See more here:
Once HangFire is Installed you can configure it to use the dashboard, which is an interface where you can find all the information about your background jobs. In the company I work, we used HangFire several times with recurring jobs mostly to import users, synchronize information across applications and perform operations that would be costly to run during business hours, and the Dashboard proved to be very useful when we wanted to know if a certain job was running or not. It also uses CRON to schedule the operations.
A sample of we are using right now is:
Startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
//Get the connection string of the HangFire database
GlobalConfiguration.Configuration.UseSqlServerStorage(connection);
//Start HangFire Server and enable the Dashboard
app.UseHangfireDashboard();
app.UseHangfireServer();
//Start HangFire Recurring Jobs
HangfireServices.Instance.StartSendDetails();
HangfireServices.Instance.StartDeleteDetails();
}
}
HangfireServices.cs
public class HangfireServices
{
//.. dependency injection and other definitions
//ID of the Recurring JOBS
public static string SEND_SERVICE = "Send";
public static string DELETE_SERVICE = "Delete";
public void StartSend()
{
RecurringJob.AddOrUpdate(SEND_SERVICE, () =>
Business.Send(), //this is my class that does the actual process
HangFireConfiguration.Instance.SendCron.Record); //this is a simple class that reads an configuration CRON file
}
public void StartDeleteDetails()
{
RecurringJob.AddOrUpdate(DELETE_SERVICE, () =>
Business.SendDelete(), //this is my class that does the actual process
HangFireConfiguration.Instance.DeleteCron.Record); //this is a simple class that reads an configuration CRON file
}
}
HangFireConfiguration.cs
public sealed class HangFireConfiguration : ConfigurationSection
{
private static HangFireConfiguration _instance;
public static HangFireConfiguration Instance
{
get { return _instance ?? (_instance = (HangFireConfiguration)WebConfigurationManager.GetSection("hangfire")); }
}
[ConfigurationProperty("send_cron", IsRequired = true)]
public CronElements SendCron
{
get { return (CronElements)base["send_cron"]; }
set { base["send_cron"] = value; }
}
[ConfigurationProperty("delete_cron", IsRequired = true)]
public CronElements DeleteCron
{
get { return (CronElements)base["delete_cron"]; }
set { base["delete_cron"] = value; }
}
}
hangfire.config
<hangfire>
<send_cron record="0,15,30,45 * * * *"></send_cron>
<delete_cron record="0,15,30,45 * * * *"></delete_cron>
</hangfire>
The CRON expression above will run at 0,15,30,45 minutes every hour every day.
Web.config
<configSections>
<!-- Points to the HangFireConfiguration class -->
<section name="hangfire" type="MyProject.Configuration.HangFireConfiguration" />
</configSections>
<!-- Points to the .config file -->
<hangfire configSource="Configs\hangfire.config" />
Conclusion
Given the scenario you described, I would probably install HangFire in your ASP.NET MVC application and remove the console application, simple because it is one project less to worry about. Even though you can install it on a console application I would rather not follow that path because if you hit a brick wall (and you'll hit, trust me), chances are you'll find help mostly for cases where it was installed in ASP.NET applications.
No need of any more console application to update the database. You can use hangfire in your MVC application itself.
http://docs.hangfire.io/en/latest/configuration/index.html
After adding the hangfire configuration, you can make use of normal MVC method to do the console operations like updating the DB.
Based on your requirement you can use
BackgroundJob.Enqueue --> Immediate update to DB
BackgroundJob.Schedule --> Delayed update to DB
RecurringJob.AddOrUpdate --> Recurring update to DB like windows service.
Below is an example,
public class MyController : Controller
{
public void MyMVCMethod(int Id)
{
BackgroundJob.Enqueue(() => UpdateDB(Id));
}
public void UpdateDB(Id)
{
// Code to update the Database.
}
}

Sub Domain implementation In MVC 5 Application

We are developing a MVC 5 application. Suppose the main site is like www.sport.com
Now we want to create 3 sub domain like cricket.sport.com, football.sport.com and hockey.sport.com
Most of the functionality will be same for this 3 sub-sites.
We have implemented 2 approaches :
Approach 1: Create 3 area for each cricket/football/hockey. But this will create code redundancy. So whenever request comes from URL we check and forward request to specific area.
Approach 2 : Create Single Controller - Check URL SubDomain and redirect depending upon each request and display specific view. In this approach, each time we need to check request sub domain and forward to same.
What is best possible way to implement sub domain in MVC application?
We want to deploy this site on Windows Azure.
What you mean by implement sub domain isn't all too clear.
If you are trying to host the same sight at different sub domains and expect them to work differently then you should have something in your web.config or some other setting that then tell each site what it is.
Something like:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="AppType" value="criket" />
</appSettings>
</configuration>
Then inside your application you'll do:
if(ConfigurationSettings.AppSettings["AppType"] == "cricket")
{
//do cricket app specific stuff
} else { /* do something else */ }
If this whole thing doesn't need to be dynamic you could also have a build symbol :
//uncomment or comment accordingly
#define CRIKET
//#define FOOTBALL
public class HomeController
{
public ActionResult Index()
{
#if CRICKET
return View("Cricket");
#elif FOOTBALL
return View("Football");
#endif
}
}
A config setting is slower than a build symbol but it gives you a dynamic approach. Choose according to your need.

How can we support modular and testable patterns with ASP.NET MVC 4 and MEF 2?

We're trying to use MEF 2 with ASP.NET MVC 4 to support an extensible application. There are really 2 parts to this question (hope that's okay SO gods):
How do we use Microsoft.Composition and the MVC container code (MEF/MVC demo source) to replace Ninject as our DI for ICoreService, ICoreRepository, IUnitOfWork, and IDbContext?
It looks like we can't use both Ninject and the MVC container at the same time (I'm sure many are saying "duh"), so we'd like to go with MEF, if possible. I tried removing Ninject and setting [Export] attributes on each of the relevant implementations, spanning two assemblies in addition to the web project, but Save() failed to persist with no errors. I interpreted that as a singleton issue, but could not figure out how to sort it out (incl. [Shared]).
How do we load multiple assemblies dynamically at runtime?
I understand how to use CompositionContainer.AddAssemblies() to load specific DLLs, but for our application to be properly extensible, we require something more akin to how I (vaguely) understand catalogs in "full" MEF, which have been stripped out from the Microsoft.Composition package (I think?); to allow us to load all IPluggable (or whatever) assemblies, which will include their own UI, service, and repository layers and tie in to the Core service/repo too.
EDIT 1
A little more reading solved the first problem which was, indeed, a singleton issue. Attaching [Shared(Boundaries.HttpRequest)] to the CoreDbContext solved the persistence problem. When I tried simply [Shared], it expanded the 'singletonization' to the Application level (cross-request) and threw an exception saying that the edited object was already in the EF cache.
EDIT 2
I used the iterative assembly loading "meat" from Nick Blumhardt's answer below to update my Global.asax.cs code. The standard MEF 2 container from his code did not work in mine, probably because I'm using the MEF 2(?) MVC container. Summary: the code listed below now works as desired.
CoreDbContext.cs (Data.csproj)
[Export(typeof(IDbContext))]
[Shared(Boundaries.HttpRequest)]
public class CoreDbContext : IDbContext { ... }
CoreRepository.cs (Data.csproj)
[Export(typeof(IUnitOfWork))]
[Export(typeof(ICoreRepository))]
public class CoreRepository : ICoreRepository, IUnitOfWork
{
[ImportingConstructor]
public CoreRepository(IInsightDbContext context)
{
_context = context;
}
...
}
CoreService.cs (Services.csproj)
[Export(typeof(ICoreService))]
public class CoreService : ICoreService
{
[ImportingConstructor]
public CoreService(ICoreRepository repository, IUnitOfWork unitOfWork)
{
_repository = repository;
_unitOfWork = unitOfWork;
}
...
}
UserController.cs (Web.csproj)
public class UsersController : Controller
{
[ImportingConstructor]
public UsersController(ICoreService service)
{
_service = service;
}
...
}
Global.asax.cs (Web.csproj)
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
CompositionProvider.AddAssemblies(
typeof(ICoreRepository).Assembly,
typeof(ICoreService).Assembly,
);
// EDIT 2 --
// updated code to answer my 2nd question based on Nick Blumhardt's answer
foreach (var file in System.IO.Directory.GetFiles(Server.MapPath("Plugins"), "*.dll"))
{
try
{
var name = System.Reflection.AssemblyName.GetAssemblyName(file);
var assembly = System.Reflection.Assembly.Load(name);
CompositionProvider.AddAssembly(assembly);
}
catch
{
// You'll need to craft exception handling to
// your specific scenario.
}
}
}
}
If I understand you correctly, you're looking for code that will load all assemblies from a directory and load them into the container; here's a skeleton for doing that:
var config = new ContainerConfiguration();
foreach (var file in Directory.GetFiles(#".\Plugins", "*.dll"))
{
try
{
var name = AssemblyName.GetAssemblyName(file);
var assembly = Assembly.Load(name);
config.WithAssembly(assembly);
}
catch
{
// You'll need to craft exception handling to
// your specific scenario.
}
}
var container = config.CreateContainer();
// ...
Hammett discusses this scenario and shows a more complete version in F# here: http://hammett.castleproject.org/index.php/2011/12/a-decent-directorycatalog-implementation/
Note, this won't detect assemblies added to the directory after the application launches - Microsoft.Composition isn't intended for that kind of use, so if the set of plug-ins changes your best bet is to detect that with a directory watcher and prompt the user to restart the app. HTH!
MEF is not intended to be used as DI framework. Which means that you should separate your "plugins" (whatever they are) composition from your infrastructure dependencies, and implement the former via MEF and the latter via whatever DI framework you prefer.
I think there are a little misunderstandings on what MEF can and can't do.
Originally MEF was conceived as purely an extensibility architecture, but as the framework evolved up to its first release, it can be fully supported as a DI container also. MEF will handle dependency injection for you, and does so through it's ExportProvider architecture. It is also entirely possible to use other DI frameworks with MEF. So in reality there are a number of ways things could be achieved:
Build a NinjectExportProvider that you can plug into MEF, so when MEF is searching for available exports, it will be able to interrogate your Ninject container.
Use an implementation of the Common Services Locator pattern to bridge between MEF and Ninject or vice versa.
Because you are using MEF for the extensibility, you'll probably want to use the former, as this exposes your Ninject components to MEF, which in turn exposes them to your plugins.
The other thing to consider, which is a bit disappointing, is in reality there isn't a lot of room for automagically plugging in of features ala Wordpress on ASP.NET. ASP.NET is a compiled and managed environment, and because of that you either resort to late-binding by loading assemblies manually at runtime, or you restart the application to pick up the new plugins, which sort of defeats the object of being able to plug new extensions in through the application.
My advice, is plan your architecture to pick up any extensibility points as startup and assume that any core changes will require a deployment and application restart.
In terms of the direct questions asked:
The CompositionProvider accepts in instance of ContainerConfiguration which is used internally to create the CompositionContainer used by the provider. So you could use this as the point by which you customise how you want your container to be instantiated. The ContainerConfiguration supports a WithProvider method:
var configuration = new ContainerConfiguration().WithProvider(new NinjectExportDescriptorProvider(kernel));
CompositionProvider.SetConfiguration(configuration);
Where NinjectExportDescriptorProvider might be:
public class NinjectExportDescriptorProvider: ExportDescriptorProvider
{
private readonly IKernel _kernel;
public NinjectExportDescriptorProvider(IKernel kernel)
{
if (kernel == null) throw new ArgumentNullException("kernel");
_kernel = kernel;
}
public override IEnumerable<ExportDescriptorPromise> GetExportDescriptors(
CompositionContract contract, DependencyAccessor dependencyAccessor)
{
var type = contract.ContractType;
if (!_kernel.GetBindings(type).Any())
return NoExportDescriptors;
return new[] {
new ExportDescriptorPromise(
contract,
"Ninject Kernel",
true, // Hmmm... need to consider this, setting it to true will create it as a shared part, false as new instance each time,
NoDependencies,
_ => ExportDescriptor.Create((c, o) => _kernel.Get(type), NoMetadata)) };
}
}
}
Note: I have not tested this, this is all theory, and is based on the example AppSettingsExportDescriptorProvider at: http://mef.codeplex.com/wikipage?title=ProgrammingModelExtensions
It's different from using the standard ExportProvider, because using the CompostionProvider is built around lightweight composition. But essentially you're wrapping up access to your Ninject kernel and making it available to your CompositionContainer.
As with adding a specific new provider (see above), you can use the ContainerConfiguration to read the available assemblies, probably something like:
var configuration = new ContainerConfiguration().WithAssemblies(AppDomain.GetAssemblies())
Again, I haven't tested all of this, but I hope it at least points you in the right direction.

Code-first always working with SQL Server Express or SQL Server CE

I'm building a MVC 3 application and use Entity Framework 4.3 code-first. My context is becoming complex so I've decided to working with SQL Server 2008 R2. Then I changed my connection string in web.config. I checked and I know the new connection string is correct, but EF is still working with the default connection string (.\SQLEXPRESS ...) from SQL Server CE (I have now deleted SQL Server CE from nuget packages).
My context name is CodeFirstContext and my connection string name is CodeFirstContext, too. But the return context.Products; always fails and/because the connection string in context says SQLEXPRESS (my web.config sets the connection string to SQLSERVER).
What is going on here? I erased all default connection strings in the project. When I am running the project, it still gets all data from SQL Server Express.
I have been searching for about two days. Why is this so difficult if so many people only have to change the connection string and then it works.
You might be picking up the connection string from your root/machine config, in your web.config file add a clear element like so
<connectionStrings>
<clear/>
.. your connection strings here
...
I 've solved the problem like that:
public class CodeFirstContext : DbContext
{
public CodeFirstContext() : base("RandevuIzle")
{
}
public DbSet<User> Users { get; set; }
}
<connectionStrings>
<add name="RandevuIzle" connectionString="Data Source=111.222.333.444\MSSQLSERVER2008; Initial Catalog=RandevuIzle;User Id=MyUserName;Password=myPassword" providerName="System.Data.SqlClient" />
Somethings went wrong but i cannot understand. I re-start the project after doing this code below. Then it's work normally.
As you instantiate your DbContext, are you providing a name that isn't CodeFirstContext?
Holy SQL Smoke, Batman! This drove me totally batty today. Getting EF 5.0 up and running w/ VS2010 on a new project was much more difficult than I imagined. I had the same problem and found this great Ode to the Code ::
http://odetocode.com/blogs/scott/archive/2012/08/15/a-troubleshooting-guide-for-entity-framework-connections-amp-migrations.aspx
And to be specific ::
public class DepartmentDb : DbContext
{
public DepartmentDb()
: base(#"data source=.;
initial catalog=departments;
integrated security=true")
{ }
public DbSet<Person> People { get; set; }
}
Got me the result I was needing. Do I think it's cool that I've got to set the data source like this and that it won't find the obvious solution within the app.config? No, I don't. But I have officially spent too much time attempting a transition from LINQ to SQL over to EF today and am going to go ahead and get some of the application done while I still have some interest in doing so.

Resources