I want to implement restriction on creating duplicated data in Asp.net MVC project.
I have a table tSectionForwardSelling (SectionForwardSellingID, StoreID, SectionID, Amount, Date).
I want to restrict a user to input duplicated data if data he wants to input in tSectionForwardSelling already has the same StoreID and SectionID. If data with same StoreID and SectionID exists, he can only edit.
I want to avoid this:
Amount Date SectionName StoreName
$1000 5/20/2015 Men Clarissa
$2345 5/20/2015 Men Clarissa
Here is my Create ActionResult from tSectionForwardSellings controller:
// GET: tSectionForwardSellings/Create
public ActionResult Create()
{
ViewBag.SectionID = new SelectList(db.tSections, "SectionID", "Section_Name");
ViewBag.StoreID = new SelectList(db.tStores, "StoreID", "Store_Name");
return View();
}
// POST: tSectionForwardSellings/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "SectionForwardSellingID,Amount,Date,StoreID,SectionID")] tSectionForwardSelling tSectionForwardSelling)
{
if (ModelState.IsValid)
{
db.tSectionForwardSellings.Add(tSectionForwardSelling);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.SectionID = new SelectList(db.tSections, "SectionID", "Section_Name", tSectionForwardSelling.SectionID);
ViewBag.StoreID = new SelectList(db.tStores, "StoreID", "Store_Name", tSectionForwardSelling.StoreID);
return View(tSectionForwardSelling);
}
And here the project itself:
https://drive.google.com/file/d/0BwgF9RnNTDDEOVlUMmxub2JxbFU/view?usp=sharing
Do you ever want that duplicated data to exist?
If the table should never contain more than 1 row for the same SectionName and StoreName values then you should solve this in the database by either creating a composite primary key (clustered index) on those 2 columns, or by creating a unique non-clustered index on those 2 columns.
Then in your .NET MVC you can also perform some checks when inserting data to check if it already exists, but you won't strictly have to, and your database still will never be able to get into a bad state.
I'll echo some of what's already been said and add a few thoughts.
First: have a constraint at the database level that prevents the duplicate scenario outright. This is usually done with a key and some types of indexes can also force this constraint.
Second: before you add anything to the database that must be unique, ask for a copy of the object from the database with those parameters, if they exist, simply update the record, if they don't, add the new item.
Third: if it's a critical item that must not under any circumstance be duplicated, make sure that for the second step you issue a lock so that no one else can do anything with that key. A lock will ensure that when you search for the item no one else will be able to then add it after you do.
In my own system I use a combination of SQL level locks and Cache based distributed locks. Either way, if it's a critical component, you will want to start to understand this sort of architecture better. In most non-critical low load scenarios you can get away with a simple look up though.
Related
This is a bit of a puzzle I'm trying to figure out.
I am working on a system where we have a number of company records saved in the database. Some of these records are duplicates and are no longer wanted/required.
However, several external systems are still mapping to these invalid records. If we were to delete them entirely it would cause errors to the systems still wanting to get the detail of that company.
The ideal workflow I would like would be;
The external system looks up Company ID X.
The current system has a table which has a record of all the remapped records, so when the request comes in, the table specifies to redirect Company ID X to Company ID Y.
There are a number of endpoints that could be altered one-by-one to do this - but it would be time-consuming, resulting in lots of repetition too.
My question is, using Entity Framework and .Net - is there a smart way of achieving this workflow?
My initial thoughts were to do something with the constructor for the company object, which repopulates the object from EF if a 'redirect' exists, but I don't know if this will play nice with navigation properties.
Would anyone have an idea?
Thanks very much.
You can create a column with foreign key for the same table to express the single unique valid company.
For example, you can add DuplicateOf column:
ALTER TABLE [Company]
ADD COLUMN [DuplicateOf] bigint NULL,
FOREIGN KEY [DuplicateOf] REFERENCES [Company] ([Id]);
and express this relation in your code:
public class Company
{
// ...
public Company DuplicateOf { get; set; }
// May be useful, hides check for duplicate logic:
public bool IsDuplicate => DuplicateOf != null;
// May be useful as well,
// returns the non-duplicate uniue company, not a duplicate, either linked or current:
public Company EffectiveCompany => DuplicateOf ?? this;
}
You will have to address EffectiveCompany when you want to work with non-duplicate and maintain this column to always point to the correct record. It will also result into additional query, if eager-loaded.
Another idea is to have a stored procedure GetCompany(bigint id) which will return the effective record - if DuplicateOf exists, or record itself otherwise. It will be good for your external systems and will let you hide all this stuff behind abstraction layer of stored procedure. If you decide to change it in future, then you can easily update it without breaking external systems.
However, for you it isn't always convenient to work with stored procedures with EF.
These are just ideas and not the best solutions, anyway.
In my opinion, the best solution would be to get rid of duplicates, update data everywhere and forget forever about this mess of duplicated data.
I've currently got a bit of an issue when trying to check values that have been posted as part of an update, to what is currently being held in the database.
Currently what i'm doing is reading the existing record into a new variable, alongside the one passed in from the post, and checking values in that variable. However I've just noticed that as soon as I read the record from the database, the values passed in from the post get reset to their previous value.
I have a feeling that the reason is that the posted record and the retrieved record both have the same primary key value, and so the code is then overwriting the new values as there can't be two different objects with the same primary key in memory. Though that is just a guess.
Can someone help me with this issue, and possibly help me find a way to get around this?
EDIT:
My code is below. This is the main code, and as soon as I retrieve the "original record", the values in the "faultrecord" get reverted to what they were before
[HttpPost]
public ActionResult Edit(fault faultrecord)
{
fault originalRecord = _faultRepository.Read(faultrecord.ID); /*here is where it gets reverted*/
if (faultrecord != originalRecord)
{
/*perform actions and update record*/
}
}
The below code is what I use to read the record from the database:
public fault Read(int id)
{
var result = ITJobEntities.faults.Where(t => t.ID == id);
if (result.Any())
{
return result.FirstOrDefault();
}
return null;
}
My only reason for believing it to be to do with the primary keys is because when I added in the "originalRecord" retrieval, my update statement to the database started failing due to there being multiple objects with the same ID (the actual error was a bit more descriptive, but I can't remember it fully).
Let's say we have a code list of all the countries including their country codes. The country code is primary key of the Countries table and it is used as a foreign key in many places in the database. In my application the countries are usually displayed as dropdowns on multiple forms.
Some of the countries, that used to exists in the past, don't exist any more, for example Serbia and Montenegro, which had the country code of SCG.
I have two objectives:
don't allow the user to use these old values (so these values should not be visible in dropdowns when inserting data)
the user should still be able to (readonly) open old stuff and in this case the deprecated values should be visible in dropdowns.
I see two options:
Rename deprecated values, for instance from 'CountryName' to '!!!!!CountryName'. This approach is the easiest to implement, but with obvious drawbacks.
Add IsActive column to Countries table and set it to false for all deprecated values and true for all other. On all the forms where the user can insert data, display only values which are active. On the readonly forms we can display all values (including deprecated ones) so the user will be able to display old data. But on some of my forms the user should be able to also edit data, which means that the deprecated values should be hidden from him. That means, that each dropbox should have some initialization logic like this: if the data displayed is readonly, then include deprecated values in dropbox and if the data is for edit also, then exclude them. But this is a lot of work and error prone too.
And other ideas?
I deal with this scenario a lot, and use the 'Active' flag to solve the problem, much as you described. When I populate a drop-down list with values, I only load 'active' data and include upto 1 deprecated value, but only if it is being used. (i.e. if I am looking at a person record, and that person has a deprecated country, then that country would be included in the Drop-downlist along with the active countries. I do this in read-only AND in edit modes, because in my cases, if a person record (for example) has a deprecated country listed, they can continue to use it, but once they change it to a non-deprecated country, and then save it, they can never switch back (your use case may vary).
So the key differences is, even in read-only mode I don't add all the deprecated countries to the DDL, just the deprecated country that applies to the record I am looking at, and even then, it is only if that record was already in use.
Here is an example of the logic I use when loading the drop down list:
protected void LoadSourceDropdownList(bool AddingNewRecord, int ExistingCode)
{
using (Entities db = new Entities())
{
if (AddingNewRecord) // when we are adding a new record, only show 'active' items in the drop-downlist.
ddlSource.DataSource = (from q in db.zLeadSources where (q.Active == true) select q);
else // for existing records, show all active items AND the current value.
ddlSource.DataSource = (from q in db.zLeadSources where ((q.Active == true) || (q.Code == ExistingCode)) select q);
ddlSource.DataValueField = "Code";
ddlSource.DataTextField = "Description";
ddlSource.DataBind();
ddlSource.Items.Insert(0, "--Select--");
ddlSource.Items[0].Value = "0";
}
}
If you are displaying the record as read-only, why bother loading the standing data at all?
Here's what I would do:
the record will contain the country code in any case, I would also propose returning the country description (which admittedly makes things less efficient), but when the user loads "old stuff", the business service recognises that this record will be read only, and you don't bother loading the country list (which would make things more efficient).
in my presentation service I will then generally do a check to see whether the list of countries is null. If not (r/w) load the data into the list box, if so (r/o) populate the list box from the data in the record - a single entry in the list equals read-only.
You can filter with CollectionViewSource or you could just create a Public Enumerable that filters the full list using LINQ.
CollectionViewSource Class
LINQ The FieldDef.DispSearch is the active condition. IEnumerable is a little better performance than List.
public IEnumerable<FieldDefApplied> FieldDefsAppliedSearch
{
get
{
return fieldDefsApplied.Where(df => df.FieldDef.DispSearch).OrderBy(df => df.FieldDef.DispName);
}
}
Why would you still want to display (for instance) customer-addresses with their OLD country-code?
If I understand correctly, you currently still have 'address'-records that still point to 'Serbia and Montenegro'. I think if you solve that problem, your current question would be none-existent.
The term "country" is perhaps a little misleading: not all the "countries" in ISO 3166 are actually independent. Rather, many of them are geographically separate territories that are legally portions or dependencies of other countries.
Also note that 'withdrawn country-codes' are reserved for 5 years, meaning that after 5 years they may be reused. So moving away from using the country-code itself as primary key would make sense to me, especially if for historical reasons you would need to back-track previous country-codes.
So why not make the 'withdrawn' field/table that points to the new country-id's. You can still check (in sql for instance, since you were already using a table) if this field is empty or not to get a true/false check if you need it.
The way I see it: "Country" codes may change, country's may merge and country's may divide.
If country's change or merge, you can update your address-records with a simple query.
If country's divide, you need a way to determine what address is part of what country.
You could use some automated system do do this (and write lengthly books about it).
OR
(when it is a forum like site), you could ask the users that still have a withdrawn country that points to multiple alternatives in their account to update their country-entry at login, where they can only choose from the list of new country's that are specified in the withdrawn field.
Think of this simplified country-table setup:
id cc cn withdrawn
1 DE Germany
2 CS Serbia and Montenegro 6,7
3 RH Southern Rhodesia 5
4 NL The Netherlands
5 ZW Zimbabwe
6 RS Serbia
7 ME Montenegro
In this example, address-records with country-id 3, get updated with a query to country-id 5, no user interaction (or other solution) needed.
But address-records that specify country-id 2 will be asked to select country-id 6 or 7 (of course in the text presented to the user you use the country-name) or are selected to perform your custom automated update routine on.
Also note: 'withdrawn' is a repeating group and as such you could/should make it into a separate table.
Implementing this idea (without downtime) in your scenario:
sql statement to build a new country-table with numerical id's as primary key.
sql statement to update address-records with new field 'country-id' and fill this field with the country-id from the new country-table that corresponds with country-code specified in that record's address-field.
(sql statement to) create the withdrawn table and populate the correct data with in it.
then rewrite your the sql statements that supply your forms with data
add the check and 'ask user to update country'-routine
let new forms go live
wait/see for unintended bugs
delete old country-table and (now unused) country-code column from the "address"-table
I am very curious what other experts think about this idea!!
I'm creating a database where users can enter some Error Reports and we can view them. I'm making these database with C# in the ASP MVC 3 .NET framework (as the tags imply). Each Error Report has a unique ID, dubbed ReportId, thus none of them are stored under the same Id. However, whenever a User creates a new Error, I pass their User Name and store it in with the rest of the report (I use User.Identity.Name.ToString() to get their name and store it as a string). I know how to get a single item from the data using a lambda expression, like so:
db.DBSetName.Single(g => g.Name == genre)
The above code is based on an MVC 3 tutorial (The Movie Store one) provided by ASP. This was how they taught me how to do it.
My major question is: is there a member function like the .Single one that will parse through the whole database and only output database entries whose stored User Name matches that of the currently logged in user's? Then, I can use this to restrict User's to being only able to edit their own entries, since only their entries would be passed to the User's View.
What would be the best way to implement this? Since the ReportId will not be changed, a new data structure can be created to store the user's Errors and passed through to the Index (or Home) View of that particular controller. From there they should be able to click any edit link, which will pass the stored ReportId back to the Edit Action of this particular controller, which can then search the entire database for it. Am I right in assuming this would work? And would this be ideal, given that the other items in the database are NOT passed through to the Index in this method, meaning the User does not have access to the other items' ReportId's, which the user needs to pass into the Edit Action for it to work? If this is ideal, this is the method that requires me to know how to parse through a database and grab every element that fits a particular description (stored User Name matches User's current User Name).
Or would a better approach be to pass the whole database to the Index View and only output the database entries that have User Name values that match the current logged in user's? I guess this could be done in a foreach loop with a nested if loop, like so:
#foreach(var item in db.Reports)
{
if(item.UserName == User.Identity.Name.ToString())
{
...code to output table...
}
}
But this passes the whole database which gives the user a lot more info than they need. It also gives them potential access to info I don't want them to have. However, I don't have to make a new data structure or database, which should lower server memory usage and fetch time, right? Or are databases passed by copy? If so, this method seems kinda dumb. However, I don't know if the first method would fracture the database potentially, this one certainly would not. Also don't remember if I NEED an else statement in C#, I'm more familiar with C++, where you don't need one and you also don't need {}'s for single line if's, if I need one: please don't judge me too harshly on it!
Small note: I am using CRUD Controllers made with the Entity First Framework in order to edit my database. As such, all creation, reading, updating, and deletion code has been provided for me. I have chosen not to add such basic, common code. If it is needed, I can add it. I will add what the Edit Action looks like:
public ActionResult Edit(string id)
{
Report report = db.Reports.Find(id);
return View(report);
}
It accepts a string as an id, ReportId is the id used and it IS a string. It is a randomly generated GUID string made with the GUID.NewGuid().ToString() function. I will also be doing the comparison of names with:
Model.UserName == User.Identity.Name.ToString()
Which was shown earlier. Sorry if this is too much text, I wanted to provide as much info as possible and not make anyone mad. If more info is needed, it can certainly be provided. So at the end of the post, the major question actually comes down to: which of the above two methods is best? And, if it's the first one, how do I implement something like that?
Thanks for your help!
Unless I'm completely misunderstanding you, you just want .Where()
Like this:
var reports = db.Reports.Where(r => r.genre == inputGenre);
This would get you an IEnumerable of Report, which you could then use however you wish.
Is there a way to validate a property that should be unique on a model? For example, a user can create and edit a "Product", but they shouldn't be able to create a Product with an existing ProductCode, nor should they be able to edit a Product and change the ProductCode to a code that already exists.
I have tried using a custom attribute.
public class Unique : ValidationAttribute
{
public override bool IsValid(object value)
{
var products = Repository.Store.Products.Where(x => x.ProductCode == value);
return (products.Count() == 0);
}
}
All that I can cover with this solution is not allowing the user to insert/update a Product when the code already exists in the DB. This does not allow the user to edit an existing product because when they submit, it will see that the code already exists(it will be the code for the product they are trying to edit.) and returns false.
Is there no way of dealing with a unique index in MVC 2, I have searched for hours, even found other topics on stackoverflow, but nothing with a solid solution.
Just let the insert or update fail and then return an appropriate error message to the user. Checking up front is problematic anyway since there's always a chance that another user will modify the database immediately after your check.
Here's an example of how you can insert an object and determine whether or not it failed due to a unique constraint:
INSERT INTO MyTable (column1, column2, column3)
SELECT #param1, #param2, #param3
WHERE NOT EXISTS (SELECT * FROM table WHERE id = #param4)
If the object already exists, this will modify 0 rows. If it does not, then it will modify 1 row. If anything else goes wrong, you'll get an exception. This is also quite efficient (at least in SQL server). It results in an index seek followed by an index update, just as you would hope.
I struggled with MVC a little in a related area.
Part of the answer gleaned to my question was that you should "probably" have a seperate model for Insert and Update of an object.
That way you could have your custom attribute just on the insert model.
Otherwise, just take care of this as a normal code check in your insert method, not in a custom attribute.
Ok I see....
Can you do a "does not equal" check on the exists for some unique ID on the object - that way you check for the existence of the product code BUT not on the current product.