how to design a custom product builder - asp.net

I am developing an application for generating estimates on products such as cars.
So, when a car make an model is selected, I need to present various options to the user (options may be in different groups like Wheels, Seating Upholstery, Trunk Accessories)
Depending upon the group, the customer may pick one or more options in that group; if a certain option is selected - some other options may get disabled; not all options apply to every make an model
So, there are several rules to be defined for different groups to indicate what combination is allowed and what is not allowed?
How should I go about designing the database for this and is there a pattern that I can leverage as I develop this application?

I solved a similar requirement with the following structure, rewritten in your terms above:
Parts
Groups
Car
With the following notes:
Parts are standalone in their own right, each with a part number.
A car template is standalone in its own right.
Parts can be added to a option group, and a number of options groups belongs to a car.
An option group cannot exist without a car.
A group can depend on another group
I need to protect against circular references
I started out playing with my model by writing the test case before i wrote the class code (Test Driven Development), which gave me code (in C#) as:
var dieselEngine = new Sku("diesel 2_litre,",1000);
var petrolEngine2 = new Sku("petrol_2_litre",800);
var petrolEngine25 = new Sku("petrol_25_litre",900);
var petrolTurbo = new Sku("petrol_turbo",2000);
var dieselTurbo = new Sku("diesel_turbo",2000);
var car = new Car("myCar");
car.AddGroup("Engines");
car.AddSkuToGroup("Engines", diselEngine);
car.AddSkuToGroup("Engines", petrolEngine2);
car.AddSkuToGroup("Engines", petrolEngine25);
car.AddGroup("Turbos");
car.AddSkuToGroup("Turbos", petrolTurbo);
car.AddSkuToGroup("Turbos", dieselTurbo);
car.SetRequirement(diselEngine, dieselTurbo);
car.SetRequirement(petrolTurbo, petrolEngine2);
car.SetRequirement(petrolTurbo, petrolEngine25);
I add the dependency option on the groups, rather than on the Sku, since a part may exist across multiple cars but may have different dependencies for each specific car.
I have to put everything through the root car object, which will check and enforce all my business rules (such as checking for and protecting against circular references).
Should all access via the car object feel clunky, you could always have the car.AddGroup function return a group to make the code make more sense to read:
var engines = car.AddGroup("Engines");
engines.AddSkuToGroup(diselEngine);
engines.AddSkuToGroup(petrolEngine2);
engines.AddSkuToGroup(petrolEngine25);
But do not forget the business rules can only be enforced by the car, since the car has visibility of all the components. So we always chain up via the root:
class ConfigurableProduct
{
List<Group> groups = new List<Group>();
Group NewGroup(string name)
{
var group = new Group(this, name);
this.groups.Add(group);
return group;
}
bool ContainsSku(string skuId)
{
foreach (var group in this.Groups)
{
if (group.ContainsSku(skuId))
return true;
}
return false;
}
}
class Group
{
Group(ConfigurableProduct parent, string name)
{
this.parent = parent;
this.name = name;
}
string name;
List<string> skuIds = new List<string>();
ConfigurableProduct parent;
void AddSkuToGroup(string skuId)
{
// enforce invariants via parent, call functions as reuqired
if (this.parent.containsSku(skuId))
throw new Exception("SKU already exists in this configurable template, cannot exist twice");
// do other things, like check circular references etc, all via this.parent
}
bool ContainsSku(string toFind)
{
foreach (var skuId in this.skuIds)
{
if (skuId == toFind)
return true;
}
return false;
}
}
For the actual database storage i would worry about persistence last, it could be a text file, MSSQL database, MySQL database, MongoDB, there are so many options.
I find it always useful to concentrate on how i want to use the code in my application, and not the specifics of how the database needs to be used, as the storage can abstracted via a repository interface that returns a class (a plain old POCO class, but in this case we have started to flesh out with business logic to protect against invalid states).
For the front end, you may want to push this all down via JSON to something like angular or knockout that can render the options available dynamically, and show or hide different elements depending on the dependencies between groups.
Working fronted example
I am not sure what front end binding you are using (or if you will only be using razor, in that case you will need to store state on the server and refresh each selection), but I have provided an example using Knockoutjs here: http://jsfiddle.net/g18c/5jt9bwsv/1/ with working dependencies and dynamic javascript object builder.
Loops through provided JSON products by group
Creates calculated fields that change depending on a target dependency
Binds to a view via knockout
The selected SKUs could then simply be passed up to the server, any business rules can also be implemented in the front end javascript.
Of course anything data that is sent from the client to the server would need to be validated by building up the product graph on the server and checking that the provided SKUs are valid (i.e. you wouldn't want to allow a Diesel Turbo to be selected for a Petrol Engine).

Related

Concurrent users and variables

Need: for multiple users to use the app at the same time, being tracked by their own request id.
I've been using datasouce parameters to hold my "current place" variable, but I'm now uncertain whether that parameter is specific to this instance or if each user is going to overwrite it.
It looks like the apps script script properties is definitely shared between users. And I thought User Properties were being deprecated for apps script anyway.
How do you ensure concurrent use of your apps don't step on each others toes?
By script properties I mean these kinds of things:
function setProp(property, value) {
var scriptProperties = PropertiesService.getScriptProperties();
scriptProperties.setProperty(property, value);
}
function getProp(property) {
var scriptProperties = PropertiesService.getScriptProperties();
var value = scriptProperties.getProperty(property);
return value;
}
whereas when I'm using datasource properties I mean:
var runID = app.datasources.requests.properties.current;

How do I retrieve Firebase data in bulk for subsequent manipulation?

I'm transitioning from SQLite to Firebase and in doing so, I have a lot of pre-existing data that I need to set into different views.
Initial view: An alphabetical list (no duplicates), showing letters for only those resources that exist below it (meaning if there is no resource that starts with X, X should not show up at all).
Secondary view: Once you tap on a letter, it expands accordion style to reveal resources (no duplicates) that start with that letter.
My Firebase query is right in that I'm getting the correct data, but how do I use it? Ideally what I'd like to do is get all the topics, drop them in a TreeSet to eliminate duplicates and sort them automatically, then do the same thing but just getting the first letter. I cant modify a set, or an array, or a list from an inner class, but I can't access the same if I instantiate from inside either (and it doesn't matter because it gets called fresh for every single object so it's always a new set).
Is there a best practice I'm missing?
mDatabase = FirebaseDatabase.getInstance();
mRootRef = mDatabase.getReference();
mQuoteRef = mRootRef.child("quotes");
Query topicQuery = mQuoteRef.orderByChild("Topic");
topicQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
TreeSet<String> topicList = new TreeSet<>(); <--obv not right
TreeSet<String> firstLetterList = new TreeSet<>();
GetQuoteInfo quote = dataSnapshot.getValue(GetQuoteInfo.class);
if (quote.getTopic() != null) {
topicList.add(quote.getTopic());
firstLetterList.add(quote.getTopic().substring(0, 1));
}
}
}
magicalListThatContainsAllOfMyFirebaseDataThatICanNowManipulate;
When you retrieve data from a Firebase database, you retrieve it as a Map, and not as a Set. So in order to solve your problem, change the way in which you retrieve the data. Because Firebase is a NoSQL database, better said a JSON database, everything is structured as key and value. So, change the Set with a Map and your problem will be solved.
Hope it helps.

Orchard CMS - determining if Model is valid in Content Item Driver

In my instance of Orchard, I have a custom content type which has a custom content part. In the "editor driver" for the content part, I need to check to see if the container content item is valid (i.e. passes validation).
The normal ModelState won't work here because of how Orchard works - and I can determine if the content part is valid, but I need to know about the entire content item (there are other content parts within the content item).
I know there are ways to execute code once a content part is published / created using lifecycle events (http://docs.orchardproject.net/Documentation/Understanding-content-handlers), but there's no way (that I know of) to pass those events information.
Basically, I need to execute a method if the content item is valid, and I need to pass the method information contained within the ViewModel.
There may be (and probably is) a better way to do this, but I'm struggling to find a way within Orchards framework.
sample code:
//POST
protected override DriverResult Editor(EventPart part, IUpdateModel updater, dynamic shapeHelper)
{
var viewModal = new EventEditViewModel();
if (updater.TryUpdateModel(viewModal, Prefix, null, null))
{
part.Setting = viewModal.Setting;
}
//here's where I need to check if the CONTENT ITEM is valid or not, for example
if (*valid*)
{
DoSomething(viewModal.OtherSetting);
}
return Editor(part, shapeHelper);
}
Note: I am using Orchard version 1.6.
No easy way to do that from inside a driver, I'm afraid. Too early. You can access other parts by doing part.As<OtherPart>, but those may or may not be updated yet at this point.
You may try utilizing handlers and OnPublishing/OnPublished (and other) events like this:
OnPublishing<MyPart>((ctx, part) =>
{
// Do some validation checks on other parts
if (part.As<SomeOtherPart>().SomeSetting == true)
{
notifier.Error(T("SomeSetting cannot be true."));
transactions.Cancel();
}
});
where transactions is ITransactionManager instance, injected in ctor.
If you need more control, writing your own controller for handling item updates/creates is the best way to go.
In order to do that (assuming you already have your controller in place), you need to use handler OnGetContentItemMetadata method to point Orchard to use your controller instead of the default one, like this:
OnGetContentItemMetadata<MyPart>((context, part) =>
{
// Edit item action
context.Metadata.EditorRouteValues = new RouteValueDictionary {
{"Area", "My.Module"},
{"Controller", "Item"},
{"Action", "Edit"},
{"id", context.ContentItem.Id}};
// Create new item action
context.Metadata.CreateRouteValues = new RouteValueDictionary {
{"Area", "My.Module"},
{"Controller", "Item"},
{"Action", "Create"});
});

Changing website according to subdomain

I have a website written in ASP.NET.
I would like to add subdomains of states. such as nevada.mysite.com.
However, I'd like my whole site to be custom made for that subdomain.
That is, I'd like to capture in each page which subdomain context I am and show different things.
I do not want to seperate to different websites. I want them all to reside in the same website in the IIS
what is the best and proper way of handling such issue?
where do you suggest to hold and save the global variable of the state?
Thanks!
First, have all of your subdomains point to a single IP address in DNS.
Next, configure IIS to listen for all connections on that IP (don't specify a host name)
Then write an HttpModule (or perhaps use Global.asax) with a handler for the BeginRequest event. Extract the subdomain name from the incoming URL, and store it in HttpContext.Items["state"] (the Items Dictionary is unique per request).
This is a great question. I've done this before except I didn't use sub domains, I used different URL's, but they still used the same code and database. I wanted a way to integrate this a bit more tightly with my LINQ to SQL code without having to type in where clauses on each one. Here's what I did:
public static IEnumerable<T> GetDataByDomain<T>(
IQueryable<T> src) where T:IDbColumn
{
//1 == website1
//2 == website2
//3 == both
string url = HttpContext.Current.Request.Url.Host;
int i = url == "localhost"
|| url == "website1.com"
|| url == "www.website1.com" ? 1 : 2;
return src.Where(x => x.domainID == i|| x.domainID == 3);
}
Basically when querying a table with LINQ to SQL I have my own custom where clause.
Used like so:
using (var db = new MyDataContext())
{
var items = Utility.GetDataByDomain(db.GetTable<Item>()).Where(x => x.isVisible);
}
Finally in each table where I had data that needed to be specified for one web site or both I added a column that took a value of 1,2 or 3(both). Additionally in my LINQ data context I made a partial class and referenced my interface:
public partial class Item : Utility.IDbColumn
{
}
The reason we need the following interface is because the first method takes an unknown type so obviously I can't select a property from an unknown type unless I tell it that any type I pass to it relies on an interface which contains that property.
Interface:
public interface IDbColumn
{
int domainID { get; set; }
}
It's kind of an interesting system, probably could have done it in many different ways. Built it a while ago and it works great.

Entity Framework: Many to Many Relationship

I have two tables with a Many-To-Many relationship like this:
User(emailaddress, Name)
UserAlerts(emailaddress, AlertId)
Alert(AlertId,Title)
Alerts have already been added to the database. When inserting a new user, I am doing a lookup on the AlertRepository. The problem is, Instead of creating a record in the User and the UsertAlerts tables only, its also adding an extra Alert record.
I am using the following code:
public ActionResult Register(UserModel model, int[] Alerts)
User user = new MidTier.Models.User();
user.Name = model.Name;
user.EmailAddress = model.EmailAddress;
if (Alerts!=null)
{
IRepository<Alert> alertRepository = new AlertRepository();
foreach (int alertId in Alerts)
{
Alert alert = alertRepository.First(a=>a.ID== alertId);
alertRepository.Detach(alert);
if (alert != null)
{
alert.Enabled = true;
user.Alerts.Add(alert);
}
}
}
userRepository.Attach(user);
userRepository.Add(user);
userRepository.Save();
Why don't you try to search little bit before you ask a question? This problem is asked several times per week. In your previous question I said you that you should use same context for loading Alert and storing User. You didn't do it and complicated whole situation.
The context doesn't know anything about existence of the alert. Once you call Add for user it will add all entities which are not tracked yet. There are three ways to solve this:
Use the same context in both repositories and do not detach alerts. Because of loading alerts, context will know about their existence and doesn't insert them again.
If you don't use the same context for loading you must attach the Alert to the new context before you add it to User. That is hard to do when you wrap EF code to repositories.
If you don't use the same context and you will not attach Alert to the new context before you add it to User you must modify your Add method for User and after adding User to the context you must iterate every alert and change its state to Unchanged.

Resources