1

Currently my code, is not too fancy.

A Meeting ID value has to be present for each row. If there is no Meeting Id in meeting object, then return error and stop all process (Meeting Id is the same for each row in the list).

Agenda Item is required, and it cannot be a duplicate value. If missing, or duplicate, return error.

If Legistar Id is missing, then the value of Agenda Item will be assigned to the missing Legistar Id.

Neither Agenda Item, and/or Legistar Id, can contain duplicate values. Each value for these properties has to be unique. Otherwise, stop, return error.

The List I am trying to pass, and validate looks like:

AgendaItem  LegistarID  Title
1   74490   Public Comment. (October 31 2022)
2   74491   Welcome Affirmative Action Commission Appointment. <-- Agenda Item 2
2   73403   Nomination Open Update.                            <-- Agenda Item 2
4   74490   Communication Strategy Update.                     <-- Legistar Id same as row 1

How can I pass a message specifying such particular cases so that the client app, receives it an alerts the user?

This is my current version of my ImportData routine, here goes to nothing

public ActionResult ImportData(List<MeetingAgendaXref_WIP> meeting) // see comment above procedure name
{

    bool status = false;
    string message = "";

    try
    {
        if (ModelState.IsValid)
        {
            var errors = new List<string>();
            var rowCounter = 1;

            using (ClerkEntities db = new ClerkEntities())
            {
                foreach (var i in meeting)
                {

                    if (i.MeetingID == 0)
                    {

                        message = string.Format("No Meeting ID. Please make sure that the Cross Reference meets the required criteria for each row before clicking the ""Upload"" button.");
                        // log error to list
                        errors.Add($"Row {rowCounter}: No Meeting ID. Please make sure that the Cross Reference meets the required criteria for each row before clicking the ""Upload"" button.");
                        return new JsonResult { Data = new { status = status, message = message } };

                    }

                    if (i.AgendaItem == 0)
                    {
                        message = string.Format("No Agenda Item. Please make sure that the Cross Reference file meets the required criteria for each row before clicking the ""Upload"" button.");
                        // log error to list
                        errors.Add($"{rowCounter}:No Agenda Item. Please make sure that the Cross Reference file meets the required criteria for each row before clicking the ""Upload"" button.");
                        return new JsonResult { Data = new { status = status, message = message } };
                    }

                    // if Legistar ID, 
                    if (i.LegistarID == 0)
                    {
                        // and Agenda Item are not present, return error message, log error message
                        if (i.AgendaItem == 0)
                        {
                            message = string.Format("Agenda Item, and Legistar Id are missing. Please make sure that the Cross Reference file meets the required criteria for each row before clicking the ""Upload"" button.");
                            // log error to list
                            errors.Add("Agenda Item, and Legistar Id are missing. Please make sure that the Cross Reference file meets the required criteria for each row before clicking the ""Upload"" button.");
                            return new JsonResult { Data = new { status = status, message = message } };
                        }
                        // otherwise, if legistar id, is empty, but agenda item is not, then assign Agenda Item to Legistar Id.
                        else
                        {
                            i.LegistarID = i.AgendaItem;
                        }
                    }

                    var compositeKey = db.MeetingAgendaXref_WIP.Find(i.MeetingID, i.AgendaItem);

                    if (compositeKey == null)
                    {
                        // Add new
                        db.MeetingAgendaXref_WIP.Add(i);

                    }
                    else
                    {
                        // Update previously saved values (same or new)
                        db.Entry(compositeKey).CurrentValues.SetValues(i);
                        db.Entry(compositeKey).State = EntityState.Modified;
                    }
                    rowCounter++;
                }
                
                // If there are errors do not save and return error message
                if (errors.Count > 0)
                {
                    return new JsonResult { Data = new { status = status, message = string.Join("\n", errors) } };
                }
                else
                {
                    db.SaveChanges();
                    message = string.Format(@"Your Cross Reference file has been uploaded successfuly!");
                    status = true;
                    return new JsonResult { Data = new { status = status, message = message } };
                }
            }
        }
        else
        {
            message = string.Format(@"Please make sure that the Cross Reference file meets the required criteria for each row before clicking the ""Upload"" button.");
            return new JsonResult {Data = new {status = status, message = message}};
        }
    }
    catch (System.ArgumentException ex)
    {
        status = false;
        ExceptionLogging.WriteLogError(ex.ToString());
    }
    return new JsonResult {Data = new {status = status, message = message}};
}
VLAZ
  • 26,331
  • 9
  • 49
  • 67
erasmo carlos
  • 664
  • 5
  • 16
  • 37
  • you have noticed in your sample, LegistarID on row 1 is: 74490 and on row 4: 73490 Thus NOT identical? Is you need an id to be unique, simply put your lists into Dictonary<> Types, You key is the id (or composite id's), and the content, your value. Now your dataset CANNOT have duplicate values. This should be the focus of your code, rather than filtering out scenarios you to want to exist, that exist. It will cause you such a headache to store data that cannot be duplicate, with duplicates.... – Morten Bork Nov 04 '22 at 07:45
  • Thank you for sharing your knowledge Morten Bork. I posted question before being completely done checking for typos. I am willing to learn and try any recommendations. If I read your post correctly, I am understanding that you are suggesting to instead of focusing on the C# code, to do a more thorough validation in the client code? Kindly, thanks. – erasmo carlos Nov 04 '22 at 07:55
  • What I am suggesting: When you store the values into a data-structure (Could be in C#, but could be client side) You use a data-structure that cannot contain duplicates. (Like if you stored it in a database, you would have an uniqueness requirement on some columns) I suggest you use a data-structure like that, so you cannot insert invalid data into your structure at all. That way, you will not have to do a validation on reading from your data structure. so your: List becomes Dictionary Extremely briefly said :D – Morten Bork Nov 04 '22 at 10:25
  • There is a datastructure called Tuple in C#, I would recommend you also look into that, and see if it either works with your Dictionary setup. https://stackoverflow.com/questions/955982/tuples-or-arrays-as-dictionary-keys-in-c-sharp Something like what was suggested here. – Morten Bork Nov 04 '22 at 12:07
  • Please edit your question to narrow down and isolate your problem in a concise example. As for now it looks like a whole production task to solve by the community. There is too little a chance anyone else could have a similar problem and could benefit from looking at the answers to your question asked in this form. – George Polevoy Nov 08 '22 at 12:00

3 Answers3

3

So basically there are total 3 validations that we need to validate on these fields (MeetingID, AgendaItem, LegistarID) as part of this requirement. As I am not aware about asp.net syntaxes. I am demoing the solution here in pure JavaScript format just to give you an idea how it will work. You can do it accordingly as per the specific language.

I am just converting the above table data into an array of objects where each object will define a row data.

[{
  MeetingID: 102,
  AgendaItem: 1,
  LegistarID: 74490,
  Title: 'Public Comment. (October 31 2022)'
}, {
    MeetingID: 102,
  AgendaItem: 2,
  LegistarID: 74491,
  Title: 'Welcome Affirmative Action Commission Appointment.'
}, {
    MeetingID: 102,
  AgendaItem: 2,
  LegistarID: 73403,
  Title: 'Nomination Open Update.'
}, {
    MeetingID: 102,
  AgendaItem: 4,
  LegistarID: 74490,
  Title: 'Communication Strategy Update.'
}]

Live Demo (All the descriptive comments has been added in the below code snippet) :

const arr = [{
  MeetingID: 102,
  AgendaItem: 1,
  LegistarID: 74490,
  Title: 'Public Comment. (October 31 2022)'
}, {
  MeetingID: 102,
  AgendaItem: 2,
  LegistarID: 74491,
  Title: 'Welcome Affirmative Action Commission Appointment.'
}, {
  MeetingID: 102,
  AgendaItem: 2,
  LegistarID: 73403,
  Title: 'Nomination Open Update.'
}, {
  MeetingID: 102,
  AgendaItem: 4,
  LegistarID: 74490,
  Title: 'Communication Strategy Update.'
}];

// Condition to check if each object in an array contains the MeetingID.
const meetingIdCondition = arr.every(({ MeetingID }) => MeetingID);

const agendaItemArr = arr.map(({ AgendaItem }) => AgendaItem);
const checkForDuplicateAgendaItem = agendaItemArr.filter((item, index) => agendaItemArr.indexOf(item) !== index);

// Condition to check if each object in an array contains the AgendaItem and should be unique.
const agendaItemCondition = arr.every(({ AgendaItem }) => AgendaItem) && !checkForDuplicateAgendaItem.length;

const legistarIdArr = arr.map(obj => {
    if (!obj.LegistarID) {
    obj.LegistarID = obj.AgendaItem;
  }
  return obj.LegistarID;
});
const checkForDuplicateLegistarId = legistarIdArr.filter((item, index) => legistarIdArr.indexOf(item) !== index);

// Condition to check if each LegistarId should be unique.
const legistarIdCondition = !checkForDuplicateLegistarId.length;

// Here is the final code to check if any of the validation fails.
if (!meetingIdCondition || !agendaItemCondition || !legistarIdCondition) {
    console.log('validation failed');
} else {
    console.log('all validation passed');
}
Debug Diva
  • 26,058
  • 13
  • 70
  • 123
0

"Check if an Array contains Duplicates with some()

To check if an array contains duplicates:

  1. Use the Array.some() method to iterate over the array.
  2. Check if the index of the first occurrence of the current value is NOT equal to the index of its last occurrence.
  3. If the condition is met, then the array contains duplicates."
    const arr1 = ['a', 'a'];
    const arr2 = ['a', 'b'];    
    function containsDuplicates(array) {
      const result = array.some(element => {
        if (array.indexOf(element) !== array.lastIndexOf(element)) {
          return true;
        }
        return false;
      });
    return result;
    }
    console.log(containsDuplicates(arr1)); // ️ true
    console.log(containsDuplicates(arr2)); // ️ false

https://bobbyhadz.com/blog/javascript-check-if-array-contains-duplicates#check-if-an-array-contains-duplicates-with-some

0

To me it sounds scary to rely on the client to send valid data, as chances are that you can't prevent data to arrive from arbitrary sources. So, every backend should validate incoming data, if it isn't hidden in a private network with it's clients.

You can check for non-unique entries in your properties by using LINQ's GroupBy like so:

// for demo purpose define a "Meeting" record and create a few meetings with random numbers
public record Meeting(int MeetingId, int AgendaItem, int LegistarId, string Title) { }

var rnd = new Random();  
var meetings = Enumerable.Range(1, 100)
    .Select(n => new Meeting(1, rnd.Next(200), 1000 + rnd.Next(200), $"Meeting #{n}"));


// check for doubles among the "AgendaItem" values
var agendaDoubles = meetings.GroupBy(m => m.AgendaItem).Where(m => m.Count() > 1);

// check for doubles among the "LegistarId" values
var legistarDoubles = meetings.GroupBy(m => m.LegistarId).Where(m => m.Count() > 1);

For every non-unique value the GroupBy will give you a structure like this:

enter image description here

with Key being the AgendaItem which is not unique and Grouping containing all records, which have the corresponding AgendaItem value.

Now you can create a message listing all non-unique AgendaItems like this:

var agendaDoublesAsText = string.Join(',', agendaDoubles.Select(m => m.Key));

You still might have to deal with the fact, that an AgendaItem that is used as replacement for a LegistarId value of "0" might already exist in the non-zero LegistarIds, but that could be easily achieved by replacing the values before the doubles check.

A remark on your code: The inner if in

// if Legistar ID,
if (i.LegistarID == 0)
{
   // and Agenda Item are not present, return error message, log error message
   if (i.AgendaItem == 0)
...

is obsolete as it has already been checked in the preceeding if statement and will never be true.

Jürgen Röhr
  • 886
  • 6
  • 10
  • Thank you for pointing that out. Is this a better way to do it:if (i.LegistarID == 0){if (i.AgendaItem == 0){message = string.Format($"Row {rowCounter}: Meeting has no LegistarID and no Agenda Item. Please check your data and try again."); return new JsonResult { Data = new { status = status, message = message }};} else{i.LegistarID = i.AgendaItem;}} – erasmo carlos Nov 17 '22 at 06:56
  • I am sorry, I do not understand how to implement your example. What is: public record Meeting(int MeetingId, int AgendaItem, int LegistarId, string Title) { } - in my context? do I have to create a separate class called 'record'? What do you mean by "hat could be easily achieved by replacing the values before the doubles check." does that indicate that I should attempt your suggestion, after: i.LegistarID = i.AgendaItem; - sorry, I am just confused, and overwhelmed. – erasmo carlos Nov 17 '22 at 07:29
  • @erasmocarlos The first 3 commands in my code are just for setting up list of meetings - just to have a runnable example. You don't need them in your code. – Jürgen Röhr Nov 17 '22 at 17:05
  • @erasmocarlos A `record` is a C# construct like class or struct (see [docs](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records)). My `Meeting` corresponds to your `MeetingAgendaXref_WIP`. – Jürgen Röhr Nov 17 '22 at 17:06
  • @erasmocarlos When no `LegistarID` is present on your data, you substitute it with the `AgendaItem`. If you don't want doubles, you have to make sure that the `AgendaItem` is not contained in your `LegistarID`s. You can achieve that by doing the substitution before the comparicons/group by command. – Jürgen Röhr Nov 17 '22 at 17:10