Sub Domain implementation In MVC 5 Application - asp.net-mvc

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.

Related

Configurable Application Insights Instrumentation Key

How can I best make the Application Inisghts' Instrumentation Key configurable in a way that allows an Azure Administrator to manage the settings for an App Services deployment of an MVC5 web application? Is there a certain event in an MVC application initialization where this should be done or is it okay to do it at pretty much any point? I am using the Trace Listener integration as well.
By default, the Instrumentation Key (iKey) is set in the ApplicationInsights.config file. Additionally, if you include the JavaScript portions, the iKey is again set in the _Layout.cshtml file. This is two different places with an iKey that you need to manage.
I want to be able to manage this key via the App Services -> Application settings tab of the Azure Portal. The reasons are:
I want to deploy multiple instances of this applications, each with its own unique iKey
I want to change this iKey periodically (because reasons)
I don't want this iKey stored in our code repository (it's okay for a "dev" iKey to be in code repo) nor do I want it to be managed by our build automation (again, because reasons)
Here is the implementation that I am currently using, and it seems to work. However, I had other implementations that seemed to set the iKey either too early or too late as it seemed it would use the iKey in the physical web.config file deployed to Azure instead of pulling from the Application settings tab from the Portal. Are there better options to do this in a best practice sort of way?
ApplicationInsights.config
<!-- Find the following node and *remove* it. It will have a GUID in it.
If you leave this, you may receive some errors even with all of the
other changes. -->
<InstrumentationKey>{GUID HERE}</InstrumentationKey>
Global.asax.cs
protected void Application_Start()
{
// Add this first line below
Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.Active.InstrumentationKey =
ConfigurationManager.AppSettings["ai:InstrumentationKey"];
// Showing the rest of this so you can see the order of operations
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
AutomapperConfig.Init();
}
web.config
<!-- Add the following to <appSettings> and put your iKey value in here. -->
<add key="ai:InstrumentationKey" value="*****" />
_Layout.cshtml (in the <head> section of the HTML. NOTE TO FUTURE READERS: I recommend you don't use this entire snippet but instead just use the line that begins instrumentationKey: and integrate that line into whatever the modern version is for the rest of this JS snippet!):
<script type = 'text/javascript' >
var appInsights=window.appInsights||function(config)
{
function r(config){ t[config] = function(){ var i = arguments; t.queue.push(function(){ t[config].apply(t, i)})} }
var t = { config:config},u=document,e=window,o='script',s=u.createElement(o),i,f;for(s.src=config.url||'//az416426.vo.msecnd.net/scripts/a/ai.0.js',u.getElementsByTagName(o)[0].parentNode.appendChild(s),t.cookie=u.cookie,t.queue=[],i=['Event','Exception','Metric','PageView','Trace','Ajax'];i.length;)r('track'+i.pop());return r('setAuthenticatedUserContext'),r('clearAuthenticatedUserContext'),config.disableExceptionTracking||(i='onerror',r('_'+i),f=e[i],e[i]=function(config, r, u, e, o) { var s = f && f(config, r, u, e, o); return s !== !0 && t['_' + i](config, r, u, e, o),s}),t
}({
instrumentationKey:'#(Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.Active.InstrumentationKey)'
});
window.appInsights=appInsights;
appInsights.trackPageView();
</script>
All of the methods you specified are great. Our recommendation is to use a web.config app setting and using this in the global.asax.cs for standard initialization. No telemetry will be sent before the initlization as we hook into OnBeginRequest().
https://learn.microsoft.com/en-us/azure/application-insights/app-insights-api-custom-events-metrics#a-namedynamic-ikeya-dynamic-instrumentation-key
Another method that might work well is to set the APPINSIGHTS_INSTRUMENTATIONKEY environment variable as it's picked up by the SDK. Of course that depends on if you have multiple apps on the same machine.
https://github.com/Microsoft/ApplicationInsights-dotnet/blob/v2.2.0/src/Core/Managed/Net40/Extensibility/Implementation/TelemetryConfigurationFactory.cs#L22

Can I pass other components into Castle Windsor config?

Lets say I have a main component that I want to initialize in a specific way and I have it's constructor take an Interface for this purpose. Is there a way to define the implementation I want for this interface in my xml and in turn inject that into the main component as a parameter? Like this:
public interface IComponent2 {
void DoStuff();
}
public class ConcreteCompImpl2 : IComponent2 {
IComponent1 _comp;
public ConcreteCompImpl2(IComponent1 comp) {
_comp = comp;
}
public void DoStuff(){
//do stuff
}
}
<component id="component1" service="ABC.IComponent1, ABC" type="ABC.ConcreteCompImpl1, ABC" />
<component id="component2" service="ABC.IComponent2, ABC" type="ABC.ConcreteCompImpl2, ABC" >
<parameters>
<component1>???</component1>
</parameters>
</component>
Or am I thinking about this all wrong and there is a much simpler way to accomplish this? The main thing I want to be able to do is configure what 'kind' of IComponent1 will get injected whenever an IComponent2 is created. Thanks
If you have only one concrete class implementing IComponent1, then it will automatically be injected when you resolve IComponent2.
If you have several classes implementing IComponent1 and want a specific one every time IComponent2 is resolved, you need to specific an inline dependency:
container.Register(
Component.For<IComponent2>()
.ImplementedBy<Component2>()
.DependsOn(Dependency.OnComponent<IComponent1, YourSpecialComponent1>())
);
I'm not completely sure you can specify this in the XML configuration, but honestly you should use the Fluent API instead of the XML configuration unless you have a really compelling reason to use it. As mentioned in the above link:
Ability to register components in XML is mostly a leftover from early days of Windsor before Fluent Registration API was created. It is much less powerful than registration in code and many tasks can be only accomplished from code.

how to modify a DbSet at an specific time?

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.

How to connect interface<T> and implementation<T> in autofac .config file

I have following class
namespace MyApplication.Services
{
public class TagEqualityComparer : IEqualityComparer<Tag>{/*code goes here*/}
}
Now what I would like to register it in autofac using .config file based configuration:
<autofac>
<components>
<component type="MyApplication.Services.TagEqualityComparer, MyApplication.Services"
service="System.Collections.Generic.IEqualityComparer, mscorlib" />
</components>
</autofac>
I have already spent whole sunday searching for solution I failed to find any. Other registrations, non templates, works perfectly but this one refuses.
Any idea how to solve it?
You need to define the rest of the generic type to specify the parameter type. Something like this:
<component type="MyApplication.Services.TagEqualityComparer, MyApplication.Services"
service="System.Collections.Generic.IEqualityComparer`1[[MyApplication.Services.Tag, MyApplication.Services]], mscorlib" />

asp.net MVC: localization

I have my target language in Session["lang"], which is either "en" or "it". I have added this to the Site.master:
<script runat="server">
void Page_Load(object sender, EventArgs e) {
string lang = Session["lang"].ToString();
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture(lang);
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.CreateSpecificCulture(lang);
}
</script>
Then I'd like to invoke a resource string like this:
<asp:Label ID="Label1" runat="server" Text="<%$ Resources:Global, test %>"></asp:Label>
I have two files in the App_GlobalResources, named Global.resx and Global.en.resx.
The problems is that no matter what is in the lang variable, I always get the results from the main Global.resx, and I never get the english version from Global.en.resx
I am doing this wrong entirely??
I tried putting the System.Threading... part in the Application_PreRequestHandlerExecute method in Global.asax.cs but the result was the same.
Thanks
PS: I am asking about a way to make this work in a simple way. If I was to use the complicate way, I'd go with this: http://helios.ca/2009/05/27/aspnet-mvc-and-localization/
i had the same dilema(how to implement localization) in my asp.net mvc app.
I followed the instructions posted here and it works like a charm.
So i created a folder named Localization under Content and then i create Resources resx files for each language i want to translate. Keep in mind that there is a convention for the resx file names. ie
Resources.resx is the default fall back for everything.
Resources.en-GB.resx is for english GB
Resources.en-US.resx is for english US
etc.
Just make sure you follow the instructions posted in the link to embed and make the Resources available in all places in your app (views, controllers etc)
Edit:
I want to add that i ommited this line from web.config since i wanted to manually set the local from my app.
<globalization uiCulture="auto" culture="auto"/>
Instead i have created the following class:
public class SmartController : Controller
{
public SmartController()
{
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
}
}
All controllers inherit from this class.
Since this is an administrative set of the locale i have to set it from my apps settings. You could read it from Cookies and set it, or otherwise. This is imo the simplest solution for localization that i have encountered so far.
Once implemented you can refer to any string you add by the following simple line of code, no extra code needed.
<%= Resources.Strings.TranslatedTerm %>
I bet this one is a duplicate.
Anyway - all you need is here (assuming that you are using webforms viewengine (might work with others too, haven't investigated)).
Oh well... here goes my 'summary':
Helpers are just a part. You need to do some modifications with your default view engine too . On createview/createpartialview it should return localizationwebformview which adds a path key to viewdata which is used by htmlhelper to find resourceexpressionsfields and pass them to localizationhelpers class which retrieves desired value.
Little bonus=>
This might be handy if you don't want to recreate resource folders for view subfolders
(in case you modify viewengine.view/partialviewlocationformats):
private static string ReformatVirtualPath(string virtualPath)
{
//This allows NOT to duplicate App_localResources directory
// ~/Views/Shared/Partial/Some/BulltihS/_View.ascx
// turns into =>
// ~/Views/Shared/_View.ascx
var start = #"(~(/?\w*/?){2})";
var end = #"(\w*.as(c|p)x)";
start = Regex.Match(virtualPath, start).Value;
end = Regex.Match(virtualPath, end).Value;
return start + end;
}
usage:
internal static ResourceExpressionFields GetResourceFields
(string expression, string virtualPath)
{
virtualPath = ReformatVirtualPath(virtualPath);
var context = new ExpressionBuilderContext(virtualPath);
var builder = new ResourceExpressionBuilder();
return (ResourceExpressionFields)
builder.ParseExpression(expression, typeof(string), context);
}
EDIT:
but it might be a good idea to avoid App_GlobalResources and App_LocalResources as K. Scott Allen suggests (check Konstantinos answer).

Resources