I have a requirement to make our current web application configurable based on client profile. So basically, allowing the application to scale and customize itself based on who the customer is. My current requirement to start off with will be simple, which is to make text within the web application configurable. So ATM, there will be 2 possible profiles, and based on which profile you select (either through AppSetting or database), all labels need to render accordingly. I can think of many ways of doing this. One thing I don't want to do is storing the label values in a database table because ATM there is no requirement to modify the labels through an interface, so I was thinking perhaps Resource files?
Also, my next requirement will be to all features within the website to be turned on/off based on profile, so I Need to keep this into consideration. Sometimes a feature will share 90% of the logic, so it wouldnt be feasible to duplicate to the feature and make the 10% changes for that profile and then have 2 copies of the same feature with minimum differences. So I'm looking for a solution for this as well. Perhaps an overall design that would cover both requirements?
Any advice will be highly appreciated.
Thanks
According to my understanding you need to update labels in the website and provide some features based on the profile selected.
So to do this , I would like to do this:
First implement MVC pattern, where our website will be in View , Model will be the Profile class and controller will host all the business logic.
If we don't want to use Database, we can serialize the object of Profile class and store (for ref
), in that we can have a File object which is a config file, where we can store the names of the features available to that profile.
At run time read all the features available for that profile and hence populate the view(website). This can be done by using either Inversion of Control Pattern. Like this:
public interface IFeatures{...}
public class Feature1 implements IFeatures{...}
public class Feature2 implements IFeatures{...}
public class Profile{
private String name;
private String pwd;
private File configFile;
...
}
public class Controller{
public List<?> getFeauturesForProfile(Profile p){
List<IFeatures> features;
List<String>feautures = scanConfigFileForFeatures(p.getConfigFile());
for(String feature : features)
features = Class.forName(feature).newInstance();
return features
}
}
Since I'm familiar with only java , I have written the syntax-es in java .
Related
I have a database application that is configurable by the user - some of these options are selecting from different external plugin systems.
I have a base Plugin type, my database schema has the same Plugin record type with the same fields. I have a PlugingMananger to load plugins (via an IoC container) at application start and link them to the database (essentially copies the fields form the plugin on disk to the database).
public interface IPlugin
{
Guid Id{ get; }
Version Version { get; }
string Name { get; }
string Description { get; }
}
Plugins can then be retrieved using PlugingMananger.GetPlugin(Guid pluginId, Guid userId), where the user ID is that of the one of the multiple users who a plugin action may be called for.
A set of known interfaces have been declared by the application in advance each specific to a certain function (formatter, external data, data sender etc), if the plugin implements a service interface which is not known then it will be ignored:
public interface IAccountsPlugin : IPlugin
{
IEnumerable<SyncDto> GetData();
bool Init();
bool Shutdown();
}
Plugins can also have settings attributes PluginSettingAttribute defined per user in the multi-user system - these properties are set when a plugin is retrieved for a specific user, and a PluginPropertyAttribute for properties which are common across all users and read-only set by the plugin one time when the plugin is registered at application startup.
public class ExternalDataConnector : IAccountsPlugin
{
public IEnumerable<AccountSyncDto> GetData() { return null; }
public void Init() { }
public void Shutdown() { }
private string ExternalSystemUsername;
// PluginSettingAttribute will create a row in the settings table, settingId
// will be set to provided constructor parameter. this field will be written to
// when a plugin is retrieved by the plugin manager with the value for the
// requesting user that was retrieved from the database.
[PluginSetting("ExternalSystemUsernameSettingName")]
public string ExternalSystemUsername
{
get { return ExternalSystemUsername }
set { ExternalSystemUsername = value; }
}
// PluginPropertyAttribute will create a row in the attributes table common for all users
[PluginProperty("ShortCodeName")]
public string ShortCode
{
get { return "externaldata"; }
}
public Version PluginVersion
{
get { return new Version(1, 0, 0, 0); }
}
public string PluginName
{
get { return "Data connector"; }
}
public string PluginDescription
{
get { return "Connector for collecting data"; }
}
}
Here are my questions and areas I am seeking guidance on:
With the above abstraction of linking plugins in an IoC container to database, the user can select the database field Customer.ExternalAccountsPlugin = idOfExternalPlugin. This feels heavy - is there a simpler way that other systems achieve this (SharePoint for instance has lots of plugins that are referenced by the user database)?
My application dictates at compile time the interfaces that it supports and ignores all others - I have seen some systems claim to be fully expandable with open plugins which I presume would mean lots of loosely typed interfaces and casting, is there a half-way ground between the two options that would allow future updates to be issued without recompile but still use concrete interfaces?
My plugins could contain metadata (PluginProperty or PluginSetting) and I am unsure the best place to store this, either in a plugin metadata table (would make linq queries more complex) or direct in the plugin database record row (easy linq queries PluginManager.GetPluginsOfType<IAccounts>.Where(x => x.ShortCode = "externaldata").FirstOrDefault();, which is used as best practice?
Since plugins capabilities and interfaces rely so heavily on the database schema, what is the recommended way I can limit a plugin for use with a specific schema revision? Would I keep this schema revision as a single row in a settings table in the database and update this manually after each release? Would the plugin support a maximum schema version, or would the application support a list of known plugin versions?
1) I'm sorry, but I don't know for sure. However, I'm pretty sure, in software that have data created or handled by custom plugin, they handle the plugin the way you described. The idea being, if a user load the data but is missing that specific plugin, the data doesn't become corrupted and the user isn't allowed to modify that data. (An example that comes to my minds is 3D softwares in general)
2) Only giving a very strict interface implementation, of course, highly restrict the plugin creation. (Ex.: Excel, I can't create a new cell type) It's not bad or good, it highly depends what you want from it, it's a choice. If you want the plugin creator to only access the data by some very specific pipes, limit the types of data he can create, then it goes with your design. Otherwise, if you goal is to open your software to improvement, then you should also expose some classes and methods you judge safe enough to be used externally. (Ex.: Maya, I can create a new entity type that derive from a base class, not just an interface)
3) Well, it does depends of a lot of things, no? When serializing your data, you could create a wrapper that contain all information for a specific plugin, ID, MetaData and whatever else you would judge needed. I would go that way, as it would be easier to retrieve, but is it the best way for what you need? Hard to say without more informations.
4) A good example of that is Firefox. Smaller version increment doesn't change the plugin compatibility. Medium version increment tests from a database if the plugin is still valid considering what it implements. If the plugin isn't implementing something that change, it is still valid. Major version increment requires a recompile of all plugins to use the new definition. From my point of view, it's a nice middle ground that allow devs to not always recompile, but it makes the development of the main software slightly more tricky as changes must be planned ahead. The idea is to balance the PitA (Pain in the Ass) factor between the software dev and the plugin dev.
Well... that was my long collection of 2 cents.
I have a two part application. One part is a web application (C# 4.0) which runs on a hosted machine with a hosted MSSQL database. That's nice and standard. The other part is a Windows Application that runs locally on our network and accesses both our main database (Advantage) and the web database. The website has no way to access the Advantage database.
Currently this setup works just fine (provided the network is working), but we're now in the process of rebuilding the website and upgrading it from a Web Forms /.NET 2.0 / VB site to a MVC3 / .NET 4.0 / C# site. As part of the rebuild, we're adding a number of new tables where the internal database has all the data, and the web database has a subset thereof.
In the internal application, tables in the database are represented by classes which use reflection and attribute flags to populate themselves. For example:
[AdvantageTable("warranty")]
public class Warranty : AdvantageTable
{
[Advantage("id", IsKey = true)]
public int programID;
[Advantage("w_cost")]
public decimal cost;
[Advantage("w_price")]
public decimal price;
public Warranty(int id)
{
this.programID = id;
Initialize();
}
}
The AdvantageTable class's Initialize() method uses reflection to build a query based on all the keys and their values, and then populates each field based on the database column specified. Updates work similarly - We call AdvantageTable.Update() on whichever object, and it handles all the database writes. It works quite well, hides all the standard CRUD, and lets us rapidly create new classes when we add a new table. We'd rather not change it, but I'm not going to entirely rule it out if there's a solution that would require it.
The web database needs to have this table, but doesn't have a need for the cost data. I could create a separate class that's backed by the web database (via stored procedures, reflection, LINQ-TO-SQL, ADO data objects, etc), but there may be other functionality in the Warranty object which I want to behave the same way regardless of whether it's called from the website or the internal app, without the need to maintain two sets of code. For example, we might change the logic of how we decide which warranty applies to a product - I want to need to create and test that in only one place, not two.
So my question is: Can anyone think of a good way to allow this class to sometimes be populated from the Advantage database and sometimes the web database? It's not just a matter of connection strings, because they have two very different methods of access (even aside from the reflection). I considered adding [Web("id")] type tags to the Advantage tags, and only putting them on the fields which exist in the web database to designate its columns, then having a switch of some kind to control which set of logic is used for reading/writing, but I have the feeling that that would get painful (Is this method web-safe? How do I set the flag before instantiating it?). So I have no ideas I like and suspect there's a solution I'm not even aware exists. Any input?
I think the fundamental issue is that you want to put business logic in the Warranty object, which is a data layer object. What you really want to do is have a common data contract (could be an interface in this case) that both data sources support, with logic encapsulated in a separate class/layer that can operate with either data source. This side-steps the issue of having a single data class attempt to operate with two different data sources by establishing a common data contract that your business layer can use, regardless of how the data is pulled.
So, with your example, you might have an AdvantageWarranty and WebWarranty, both of which implement IWarranty. You have a separate WarrantyValidator class that can operate on any IWarranty to tell you whether the warranty is still valid for given conditions. Incidentally, this gives you a nice way to stub out your data if you want to unit test your business logic in the WarrantyValidator class.
The solution I eventually came up with was two-fold. First, I used Linq-to-sql to generate objects for each web table. Then, I derived a new class from AdvantageTable called AdvantageWebTable<TABLEOBJECT>, which contains the web specific code, and added web specific attributes. So now the class looks like this:
[AdvantageTable("warranty")]
public class Warranty : AdvantageWebTable<WebObjs.Warranty>
{
[Advantage("id", IsKey = true)][Web("ID", IsKey = true)]
public int programID;
[Advantage("w_cost")][Web("Cost")]
public decimal cost;
[Advantage("w_price")][Web("Price")]
public decimal price;
public Warranty(int id)
{
this.programID = id;
Initialize();
}
}
There's also hooks for populating web-only fields right before saving to the web database, and there will be (but isn't yet since I haven't needed it) a LoadFromWeb() function which uses reflection to populate the fields.
I am working on a BI application in WPF. I am in the process of designing its architecture and am in search of a way to directly bind controls in the view to a xml which contains the metadata of the view. Do you think this is going to be possible? then how? or is it advisable to read off from the xml and generate the views accordingly?
Edited
Properties such as colors of charts, who created the chart, the next chart upon drilling down a chart, the user names and their passwords, user group names etc. are stored in XML files. When a user starts the application the dashboards he has created should be displayed; this happens with the retrieval of data from the back end and by assigning the correct chart colors. So if these data are available in the XML, my question is the best way to generate the charts and dashboards upon user request.
Edited
As I explained earlier as well, the problem is to store the metadata related to this application in the most efficient and structured way to call back upon a user loging in.
Thanks in advance.
I'm not sure I quite understand what you are looking to do. If you just want to bind some UI control properties to data in an XML document, that's entirely possible. I blogged about it years ago here.
I will suggest use of XAML instead of XML.
XAML will not only let you define the UI but XAML also can contain your other metadata or config information that you can read/write in the form of XAML to directly your CLR class.
Benefits are,
Xaml serialization is exactly same as that of Xml's serialization
Xaml will give you powerful intellisense while editing in Visual Studio (xml also can give but you will have to create and update schema everytime you make changes to your configuration schema)
In case of intellisense, Xaml is better because it will automatically give validation errors
It will also allow you to use Enums
It will also hide/show members or classes based on inheritance hierarchy
You can load XAML from string coming from database as well
It will let you specify bindings as well if your object is derived from DependencyObject and you will be able to transfer or reuse the bindings in your UI
For example,
public class ScreenElement{
public string Author {get;set;}
public DateTime DateCreated {get;set;}
}
// XAML can not directly deal with generics so this step is
// necessary
public class ScreenElements : ObservableCollection<ScreenElement>
{
}
[ContentProperty("Elements")]
public class Screen
{
public Screen(){
this.Elements = new ScreenElements();
}
public string Title{get;set;}
public bool ToolbarPresent {get;set;}
// this attribute is necessary if
// you want to save Screen to xaml
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ScreenElements Elements {get; private set;}
}
And your Screen xaml can look like
<Screen xmlns="clr-namespace:MyNamespace"
Title="Home Screen"
ToolbarPresent="false"
>
<ScreenElement Author="Myself" DateCreated="..."/>
<ScreenElement Author="Yourself" DateCreated="..."/>
</Screen>
You can create XAML resource and load it like...
Screen s = XamlReader.Load(.. resource uri to your XAML)
// and now you can use your "s" loaded with elements to
// populate your UI
foreach(ScreenElement e in s.Elements){
// use attributes of e to populate things..
}
I think the best in your case would be to devide all possible data in the system by data classes - metatypes. after that, in xml, specify data metatype so your data would be always have metatype. And when, before view creation, you should read all metatypes for data you are intend to display and create screen controls according to that metatypes. After that you could load and display data. Such approach works well in my small programm and I thinks it would yield good results in your system too.
[EDIT]
OK, your application includes business domain (your business data, business logic and rules for data displaying). All this things you have spread among three parts: Model, View and ViewModel. As I understand correctly your question is stright about ViewModel.
For example your hypothetical application containы employee information and suppose every employee may have three types of information about he or she:
Personal information (Name, date of birth, photo, home address, mobile phone number)
Education information (information about education, list of completed training cources)
Proffesional experience information (list of succesfully completed commercial projects)
So we have domain - employee. This domain may be devided into three metatype:
Personal metatype
Education metatype
Proffesional experience metatype
For each metatype we should create subscreen which would display metatype information according to business rules. I'll recomend you to make metatype subscreens with MVC pattern because of in case of editing of data some special editing rule or data validation may be applyed. When we have each subscreen created we can be free to display each type of meta information in the system.
For example you application have loaded employee information. After that you can determine which metatype presented in loaded data and can force creation of appropriate subscreens. The last part of work is to pass appropriate data to each sub screen.
It was very vague explanation sorry for my english, if you have any question about I have explained feel free and ask question again
I'm planning an achievement system for an ASP.NET MVC site. I want the achievement "rules" be stored in a database - to be easier to add new achievements and a central place to manage existing rules. Users will have no access to change the rules.
When a user performs an action that could potentially earn an achievement, the db rules will be queried and if there are matches, give them the achievements (stored in a lookup table, (userId, achievementId, dateAwarded).
At the moment I'm planning to put the "triggers" on certain actions in the controller, but the code that does the work will be in the model.
Is there standard DB schema for an achievement system that accomplishes this? No need to reinvent the wheel if not necessary. If not, what sorts of issues do you think would pop up, what to look out for?
You may find this answer helpful.
Speaking from experience, building a database-based rules engine for reacting to user actions is a very time-consuming, error-prone, painful exercise.
Instead, you can write any number of individual classes, each one responsible for knowing how to award one particular achievement. Use a base class or interface to give them all a common contract:
public abstract class AchievementAwarder
{
//override to provide specific badge logic
public abstract void Award();
}
You can foreach over each AchievementAwarder and call Award() on a recurring schedule. For example, you could have a "100 visits" achievement:
public class 100VisitsAwarder : AchievementAwarder
{
public override void Award()
{
//get a visit count from the db for all users
//award achievements
}
}
This solves two problems:
It's orders of magnitude simpler, but much more flexible, than a database-based rules engine. As you can see, the individual units are extremely small and easy to change without affecting the larger system.
It can run asynchronously, so for achievements that require some heavy lifting to determine if they should be awarded, the user's normal activities are not impacted by the achievements engine.
I'm pretty new to SharePoint Web Part development, so I'm not sure the best practices for handling data persistence. In my application, user postbacks will be modifying some underlying data structure that I would like to maintain rather than rebuild.
The two things I have tried so far are making objects serializable, and storing them in the session array. It seems like there should be a more elegant solution though. I have also tried using custom properties where I set the web browsable attribute to false... although I haven't tried using it for custom classes.
What is the preferred method for data persistence in webparts?
Edit: Here is an example of what I think you are suggesting. First, a class
[Serializable]
public class foo
{
public foo()
{
}
}
Then in my webpart, I would have something like...
public class WebPart : WebControls.WebParts.WebPart
{
[WebBrowsable(false)]
[WebPartStorage(Storage.Personal)]
public foo bar { get; set; }
}
The next problem I seem to have is these values don't seem to persist if I change them programatically. Anything that I expose to the user through the property panel will persist only if the user makes the change. Am I missing something?
You should take a look: ASP.NET Web Parts Overview and Web Parts Personalization Overview. I personally store webparts configuration by using WebPartStorage attribute on public properties from my webpart class.
By default, sessions are disabled on Sharepoint. You should take extra care if you plan to enable it, including performance and stress tests.