1

To start, here are my classes:

public class Name
{
    public int NameId {get;set;}
    public string Value {get;set;}
    [ForeignKey("Name_NameId")]
    public ICollection<PersonName> PersonsName {get;set;}
}

public class NameType
{
    public int NameTypeId {get;set;}
    public string Value {get;set;}
    [ForeignKey("NameType_NameTypeId")]
    public ICollection<PersonName> PersonsName {get;set;}
}

public class Person
{
    public int PersonId {get;set;}
    public string Suffix {get;set;}
    public datetime DateOfBirth {get;set;}
    [ForeignKey("Person_PersonId")]
    public ICollection<PersonName> PersonsName {get;set;}
}

public class PersonName
{
    public int Person_PersonId {get;set;}
    public int Name_NameId {get;set;}
    public int NameType_NameTypeId {get;set;}
    public int Order {get;set;}
}

Ideally, when accessing the Person class, I'd like to be able to call a function (or property) that could pull the person's full name from the PersonName repository. However, I'm not quite sure how to do that. For an example of data in these tables:

NameId               Value
1                    John
2                    Jacob
3                    Jingleheimer
4                    Schmidt

NameTypeId           Value
1                    First Name
2                    Middle Name
3                    Last Name
4                    Nickname

PersonId             Suffix                 DateOfBirth
1                                           01/01/1900

Person_PersonId      Name_NameId            NameType_NameTypeId        Order
1                    1                      1                          0
1                    2                      2                          0
1                    3                      2                          1
1                    4                      3                          0

So in the Person class, I'd like to have a function/property like GetFullName()/FullName where it would return "John Jacomb Jingleheimer Schmidt". I've been working through a tutorial from scratch and after making each class they then make an interface, mockrepository and are working on a db repository. However, I'd like to pass the information into a view via a View Model, but I'm not sure how to tie the class to the repository, etc. Is there anyone that can either point me to a tutorial that can explain that best or spell it out for me? Thanks.

XstreamINsanity
  • 4,176
  • 10
  • 46
  • 59
  • You mentioned you are working through a tutorial - can you link us to it? – chakeda Jun 25 '19 at 15:50
  • @chakeda - It's here: https://app.pluralsight.com/library/courses/aspdotnet-core-2-0-mvc-application-visual-studio-2017 In that tutorial, they aren't doing what I'm trying to accomplish above. When I follow that tutorial exactly, everything works just fine. I was using it as a start to something personal I'm trying to do. – XstreamINsanity Jun 25 '19 at 20:11
  • Here are [40 pitfalls about names](https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/). Your person should only have one nullable field for the name. That said, you are taking database normalization way to far. If you realy want to go the route, your problem is probably the missing navigation property in `PersonName`. You can then go from the `Person` via `PersonName` via `MissingNameNavigationProperty` to the `Value` property. – Christian Gollhardt Jun 30 '19 at 22:58
  • This database design is terrible unless you are in the business of "names", which would be very common. The name should not have its own table. – Tengiz Jul 01 '19 at 21:35

4 Answers4

4

In my opinion, your tables are too complicated. The draw back is that you have to join so many tables just to get someones' Full Name... and because of all the joins you would potentially face performance issues... this code would be hard to understand and maintain by other developers.

So, I know I am not answering your question directly, but I would recommend you to simplify your table design.

Why not using a simple table like:

public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set;}
    public string MiddleName { get; set;}
    public string LastName { get; set; }
    public string NickName { get; set; }
    public string Suffix { get; set; }
    public datetime DateOfBirth { get; set; }

    // it is a lot easier to implement GetFullName() method here than joining so many tables
    public string GetFullName()
    {
        retun FirstName + " " + MidleName + " " + LastName;
    }
}

I am really struggling to understand your last table: PersonName

public class PersonName
{
    public int Person_PersonId { get; set; }
    public int Name_NameId { get; set; }
    public int NameType_NameTypeId { get; set; }
    public int Order { get; set; } // <-- Why putting Order in PersonName?
}

To me, Order is not a logical column in PersonName's table... I don't fully understand your design and I could be totally wrong... but to me, it feels like an over-engineered design.

Hooman Bahreini
  • 14,480
  • 11
  • 70
  • 137
  • One of the end goals of this database will be for users to be able to search for persons as "granularly" as possible. The Order column was to be used for people who may have multiple first, middle, or last names. So, if someone's name was "Bob Henry Jackson Jones", they would have two middle names. I would have two Middle Name records for that person, one for Henry and one for Jackson, with an Order of 0 and 1 respectively. This way, I can order it properly when displaying full name, but also search for Henry as a middle name. It may be over complicated... – XstreamINsanity Jun 25 '19 at 12:46
  • You want to create so many tables just to be able to search for names individually? I feel you might be sacrificing your design because of search. You can go with the simple table that I suggested... then you can do some many things for search: you can write a procedure to search all the name columns or you can write a view on top of your table which would return the individual names... if you have more advanced search requirement then you can use one of the many free search engines like [Elasticsearch](https://www.elastic.co)... – Hooman Bahreini Jun 25 '19 at 21:24
0

I hope I understood your questions correctly

I made some assumptions though

I used your classes to construct a database and queried the database so the other tables are linked by relationship

Assuming you are passing the id of the person, and also assuming that the dbcontext is available and instantiated inside of the class, then call GetFullName

public string GetPersonById(int personId)
{
    var person = db_context.PersonNames.FirstOrDefault(m => m.Person_PersonId == personId);
    //did not check for null here
    var name = person.Person.PersonNames.ToList();
    var fullname = "";
    name.ForEach(m => fulname += m.Name.Value + " ");
    return fullname;
}

public virtual string GetFullName
{
    get { return GetPersonById(PersonId); }// the person id passed, the class if instantiated can call this instance PersonId
    set { ; }
}

Also you can get data the from the database into the models you have and the method works fine. passing them from the db give you the relationship ability and Razor Engine can be of great help in the View, but

To get the data you will need to pull out the data one after the other and store in a list.

public void UseModel()
{
    var people = _context.People.ToList();
    var names = _context.Names.ToList();
    var name_types = _context.NameTypes.ToList();

    List<PersonModel> ps = new List<PersonModel>();

    people.ForEach(m =>
    {
        ps.Add(new PersonModel
        {
            PersonId = m.PersonId,
            Suffix = m.Suffix,
            DateOfBirth = m.DateOfBirth,
            PersonsName = null//I suggest you use mapping here see https://stackoverflow.com/questions/16118085/best-practices-for-mapping-one-object-to-another
            //use mapping to map the list or use a fooloop to construct it before this declaration and assign it here
        });
    });
    //do for names and name_types

}

You can also pass this data to the View as an object PersonName that has all

Bosco
  • 1,536
  • 2
  • 13
  • 25
0

To map your view model. You can use like that.

public class PersonDTO
{
    public int PersonId { get; set; }
    public string FirstName { get; set;}
    public string MiddleName { get; set;}
    public string LastName { get; set; }
    public string NickName { get; set; }
    public string Suffix { get; set; }
    public datetime DateOfBirth { get; set; }

    public string GetFullName()
    {
        retun FirstName + " " + MidleName + " " + LastName;
    }
}

To Map FirstName , MiddleName , LastName , NickName. You can Use.

public void MapToPersonDTO(){

    var selectedPerson = _context.Person.FirstOrDefault(m => m.PersonId == 1);

    var personDto = new PersonDTO(){
        FirstName = _context.Name.FirstOrDefault(n=>n.NameId == selectedPerson.PersonsName.FirstOrDefault(f=>f.NameType_NameTypeId == 1)).Value,
        MiddleName= _context.Name.FirstOrDefault(n=>n.NameId == selectedPerson.PersonsName.FirstOrDefault(f=>f.NameType_NameTypeId == 2)).Value,
        LastName  = _context.Name.FirstOrDefault(n=>n.NameId == selectedPerson.PersonsName.FirstOrDefault(f=>f.NameType_NameTypeId == 3)).Value,
        NickName  = _context.Name.FirstOrDefault(n=>n.NameId == selectedPerson.PersonsName.FirstOrDefault(f=>f.NameType_NameTypeId == 4)).Value,
    };
}
Yigit Tanriverdi
  • 920
  • 5
  • 19
0

I am also working on this. My inspiration is the show "The First 48" on cable TV, where the "police" will have a name in their database compiling a file on an individual long before they have a person to tie to the events. Or Shawn Corey Carter takes on the name J-Zee. Or how about this one, Prince Rogers Nelson, his performing career he is known as Prince then he changed his name to a Logo. Add a column for that one. Whenever I see a suggestion to not strive for the complex, just make it simple. I always think, how is that going to represent those scenarios?

but I see the "PersonName" implementation like this given the capabilities of EF and EF Core.

public abstract class PeopleName
{
    public PeopleName()
    {
    }

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long Id { get; set; }
    [ForeignKey("Person"), Required, Column(Order = 1)]
    public long PersonId { get; set; }
    [ForeignKey("Name"), Required, Column(Order = 2)]
    public long NameId { get; set; }
    public EnumNameType NameType { get; set; }
    public byte NameOrder { get; set; }

    public virtual Person? Person { get; set; } = new Person();
    public virtual Name? Name { get; set; } = new Name();

}



public class FirstName : PeopleName
{
    [Required]
    public new EnumNameType NameType { get; set; } = 
        EnumNameType.FirstName;
}

public class LastName : PeopleName
{
}

public class FullName : PeopleName
{
}

public class Alias : PeopleName
{
}

public class MiddleName : PeopleName
{
}

public class MiddleInitial : PeopleName
{
}

The DTO I am hoping could be done along this line. I am hoping someone will show us how to load it once and have the values delineated among the properties.

public class PersonDTO
{
    public int PersonId { get; set; }
    public string? FirstName
    {
        get
        {
            var names = NamesLink.OfType<FirstName>().ToList();
            if (names.Any())
            {
                foreach (FirstName? fn in names)
                {
                    firstName = fn.Name.NameString;
                }
            }
            return firstName;
        }
        set
        {
            if (value != null)
            {
                var name = new Name { NameString = value };
                var pn = new FirstName { Name = name };
                NamesLink.Add(pn);
                firstName = value;
            }

        }
    }
    private string? firstName;
    public string? LastName
    {
        get
        {
            var names = NamesLink.OfType<LastName>().ToList();
            if (!names.Any())
            {
                foreach (LastName fn in names)
                {
                    lastName = fn.Name.NameString;
                }
            }
            return lastName;
        }
        set
        {
            if (value != null)
            {
                var name = new Name { NameString = value };
                var ln = new LastName { Name = name };
                NamesLink.Add(new LastName { Name = name });
                NamesLink.Add(ln);
                lastName = value;
            }

        }
    }
    private string? lastName;

    public DateTime DateOfBirth { get; set; }

    public string GetFullName()
    {
        return FirstName + " " + LastName;
    }
    public virtual ICollection<PeopleName> NamesLink { get; set; } = new Collection<PeopleName>();

}
ruffone
  • 23
  • 1
  • 7