2016-10-25, 16:59
  #1
Medlem
Hej,

Jag har suttit sen kl 08:00 med ett problem jag inte förstår mig på.
Så nu väljer jag att be om hjälp

Jag har skapat ett vanligt Asp.Net MVC project med ApplicationUser klasserna för inlogg osv.
Det jag försöker göra är att lägga till ytterligare kolumner i denna klass och en referens till en annan klass jag har lagt till "symptom". Klasserna ser ut såhär:

Kod:
public class ApplicationUser : IdentityUser
    {
        public Symptom Symptom { get; set; }

        [Required]
        public byte SymptomId { get; set; }

        [Required]
        public string Telephone { get; set; }

        public string Descripton { get; set; }
    }


och symptom klass:

Kod:
public class Symptom
    {
        public byte Id { get; set; }

        [Required]
        [StringLength(255)]
        public string Name { get; set; }
    }


När jag kör en code first migration så ser koden ut som sådan:

Kod:
    public partial class UpdateUserTblAddSymptom : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.AspNetUsers", "SymptomId", c => c.Byte());
            CreateIndex("dbo.AspNetUsers", "SymptomId");
            AddForeignKey("dbo.AspNetUsers", "SymptomId", "dbo.Symptoms", "Id", cascadeDelete: true);
        }
        
        public override void Down()
        {
            DropForeignKey("dbo.AspNetUsers", "SymptomId", "dbo.Symptoms");
            DropIndex("dbo.AspNetUsers", new[] { "SymptomId" });
            DropColumn("dbo.AspNetUsers", "SymptomId");
        }
    }

jag har även populerat tabellen "symptom" med data:

Kod:
public partial class PopulateSymptomTable : DbMigration
    {
        public override void Up()
        {
            Sql("INSERT INTO Symptoms (Id, Name) VALUES (1, 'Affektiva störningar')");
            Sql("INSERT INTO Symptoms (Id, Name) VALUES (2, 'Psykossjukdomar')");
            Sql("INSERT INTO Symptoms (Id, Name) VALUES (3, 'Ångestsjukdomar')");
            Sql("INSERT INTO Symptoms (Id, Name) VALUES (4, 'Ätstörningar')");
            Sql("INSERT INTO Symptoms (Id, Name) VALUES (5, 'Beroende och substansrelaterade sjukdomar')");
            Sql("INSERT INTO Symptoms (Id, Name) VALUES (6, 'Sexualitet och sexuella störningar')");
            Sql("INSERT INTO Symptoms (Id, Name) VALUES (7, 'Somatoforma och psykosomatiska sjukdomar')");
            Sql("INSERT INTO Symptoms (Id, Name) VALUES (8, 'Personlighetsstörningar')");
            Sql("INSERT INTO Symptoms (Id, Name) VALUES (9, 'Neuropsykiatriskt funktionshinder')");
        }

        public override void Down()
        {
            Sql("DELETE FROM Symptoms WHERE Id IN (1, 2, 3, 4, 5, 6, 7, 8, 9)");
        }
    }



När jag kör applikationen och klickar på register så populeras fältet symptom med rätt data, dropdownlist. Jag fyller i alla andra fält korrekt och klickar register, då dyker detta fel upp:


Kod:
Violation of PRIMARY KEY constraint 'PK_dbo.Symptoms'. Cannot insert duplicate key in object 'dbo.Symptoms'. The duplicate key value is (5).
The statement has been terminated.

I kontrollern "AccountController" så anropas post metoden Register och kraschar vid markerade raden nedan:

Kod:
//
        // POST: /Account/Register
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Register(RegisterViewModel model)
        {
            var symptom = _context.Symptoms.Single(s => s.Id == model.Symptom);
            //var status = _context.Statuses.Single(s => s.Id == model.Status);

            if (ModelState.IsValid)
            {
                var user = new ApplicationUser()
                {
                    UserName = model.UserName,
                    Telephone = model.Telephone,
                    Symptom = symptom,
                    //Status = status,
                    Descripton = model.Descripton
                };

                HÄR KRASCHAR KODEN!
                var result = await UserManager.CreateAsync(user, model.Password);
               

 if (result.Succeeded)
                {
                    await SignInAsync(user, isPersistent: false);
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    AddErrors(result);
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }


Någon som har en anning om vad jag har gjort fel?
Citera
2016-10-25, 17:43
  #2
Medlem
UserManager.CreateAsync kommer, i slutändan via sin UserStore (implementerad i Microsoft.AspNet.Identity.EntityFramework), försöka att lägga till ett DbEntitySet för din nyligen skapade användare.

Problemet är att din användare även vill lägga till ett symptom (du har valt "Beroende och substansrelaterade sjukdomar", Id 5 när du registrerade din användare).
Primärnyckeln (Id = 5) är redan upptagen, som du vet. Därför får du "Violation of PRIMARY KEY constraint", du kan inte ha två stycken entities i din databas med samma primärnyckel. Nu tror jag inte att det var meningen heller.

Det enkla sättet är att du, i din Register-metod på Account-Controllern, helt enkelt sätter Symptom-Id på din User och nullar Symptom-propertyn.

Varför har du valt byte som datatyp på din id-kolumn?
Citera
2016-10-25, 20:52
  #3
Medlem
Protons avatar
Citat:
Ursprungligen postat av Blippster

Varför har du valt byte som datatyp på din id-kolumn?
Förstår inte det heller, normalt sett hade man väl troligen valt en int som alla andra?
Citera
2016-10-26, 13:05
  #4
Medlem
Citat:
Ursprungligen postat av Blippster
UserManager.CreateAsync kommer, i slutändan via sin UserStore (implementerad i Microsoft.AspNet.Identity.EntityFramework), försöka att lägga till ett DbEntitySet för din nyligen skapade användare.

Problemet är att din användare även vill lägga till ett symptom (du har valt "Beroende och substansrelaterade sjukdomar", Id 5 när du registrerade din användare).
Primärnyckeln (Id = 5) är redan upptagen, som du vet. Därför får du "Violation of PRIMARY KEY constraint", du kan inte ha två stycken entities i din databas med samma primärnyckel. Nu tror jag inte att det var meningen heller.

Det enkla sättet är att du, i din Register-metod på Account-Controllern, helt enkelt sätter Symptom-Id på din User och nullar Symptom-propertyn.

Varför har du valt byte som datatyp på din id-kolumn?



Löste detta genom att ändra navigations properties i min klass till foreignkey properties istället och tog bort de två onödiga anropen i register post metoden.

var symptom = _context.Symptoms.Single(s => s.Id == model.Symptom);
var status = _context.Statuses.Single(s => s.Id == model.Status);

och deklererade symptom och status värden direkt, för den som kanske stöter på något liknande problem i framtiden.
Citera
2016-10-26, 13:07
  #5
Medlem
Nu när vi ändå är igång, så tänkte jag ställa en till fråga.
Jag har ett LINQ uttryck:

Kod:
var userId = User.Identity.GetUserId();

            var loggedInUser = (_context.Users
                .SingleOrDefault(u => u.Id == userId));

            var applicationUsers = _context.Users
                    .Where(u => u.StatusId == (byte)AvailableStatus.Available
                        && u.SymptomId == loggedInUser.SymptomId)
                    .Include(s => s.Symptom)
                    .Include(s => s.Status)
                    .ToList();

            var randomUser = applicationUsers[(new Random()).Next(applicationUsers.Count())];

            return View(randomUser);

Jag skulle vilja slå ihop dessa två uttrycken:

Kod:
var loggedInUser = (_context.Users
                .SingleOrDefault(u => u.Id == userId));

            var applicationUsers = _context.Users
                    .Where(u => u.StatusId == (byte)AvailableStatus.Available
                        && u.SymptomId == loggedInUser.SymptomId)
                    .Include(s => s.Symptom)
                    .Include(s => s.Status)
                    .ToList();


till ett gemensamt anrop, så jag slipper göra två anrop till servern.
Någon som kan hjälpa mig med detta?
Citera
2016-10-26, 15:18
  #6
Medlem
Sane?s avatar
Citat:
Ursprungligen postat av ElegantTD
Nu när vi ändå är igång, så tänkte jag ställa en till fråga.
Jag har ett LINQ uttryck:

Kod:
var userId = User.Identity.GetUserId();

            var loggedInUser = (_context.Users
                .SingleOrDefault(u => u.Id == userId));

            var applicationUsers = _context.Users
                    .Where(u => u.StatusId == (byte)AvailableStatus.Available
                        && u.SymptomId == loggedInUser.SymptomId)
                    .Include(s => s.Symptom)
                    .Include(s => s.Status)
                    .ToList();

            var randomUser = applicationUsers[(new Random()).Next(applicationUsers.Count())];

            return View(randomUser);

Jag skulle vilja slå ihop dessa två uttrycken:

Kod:
var loggedInUser = (_context.Users
                .SingleOrDefault(u => u.Id == userId));

            var applicationUsers = _context.Users
                    .Where(u => u.StatusId == (byte)AvailableStatus.Available
                        && u.SymptomId == loggedInUser.SymptomId)
                    .Include(s => s.Symptom)
                    .Include(s => s.Status)
                    .ToList();


till ett gemensamt anrop, så jag slipper göra två anrop till servern.
Någon som kan hjälpa mig med detta?
Eftersom den andra frågan beror på den första finns det inget enkelt sätt att göra det med EF.

Edit: Detta kanske funkar:
Kod:
            var applicationUsers = _context.Users
                    .Where(u => u.StatusId == (byte)AvailableStatus.Available
                        && u.SymptomId == _context.Users.Where(iu => iu.Id == userId).Select(iu => iu.SympthomId).FirstOrDefault())
                    .Include(s => s.Symptom)
                    .Include(s => s.Status)
                    .ToList();

var loggedInUser = applicationUsers.SingleOrDefault(u => u.Id == userId));
__________________
Senast redigerad av Sane? 2016-10-26 kl. 15:25.
Citera
2016-10-26, 16:31
  #7
Medlem
Citat:
Ursprungligen postat av Sane?
Eftersom den andra frågan beror på den första finns det inget enkelt sätt att göra det med EF.

Edit: Detta kanske funkar:
Kod:
            var applicationUsers = _context.Users
                    .Where(u => u.StatusId == (byte)AvailableStatus.Available
                        && u.SymptomId == _context.Users.Where(iu => iu.Id == userId).Select(iu => iu.SympthomId).FirstOrDefault())
                    .Include(s => s.Symptom)
                    .Include(s => s.Status)
                    .ToList();

var loggedInUser = applicationUsers.SingleOrDefault(u => u.Id == userId));

Tack, jag skall testa detta
Citera
2016-10-27, 16:34
  #8
Medlem
Sane?s avatar
Piggare idag, detta borde funka bättre:
Kod:
var applicationUsers = _context.Users
    .Where(u => 
        u.StatusId == (byte)AvailableStatus.Available && 
        u.Symptom.Users.Any(iu => iu.Id == userId))
    .Include(s => s.Symptom)
    .Include(s => s.Status)
    .ToList();

var loggedInUser = applicationUsers.SingleOrDefault(u => u.Id == userId));
Citera
2016-10-28, 16:58
  #9
Medlem
Trillskes avatar
Minitips är att inte använda Var när du hämtar instans via linq, man ser redan här i svaren att så fort någon bryter din notation (instans som lowercase av typen) blir det dåligt, och den kan vara svår att upprätthålla överallt.
Citera
2016-10-28, 18:06
  #10
Medlem
tj.s avatar
Citat:
Ursprungligen postat av Sane?
Eftersom den andra frågan beror på den första finns det inget enkelt sätt att göra det med EF.
Varför skulle det inte finnas ett enkelt sätt p.g.a. det? Något sådant här bör fungera om man vill ha det i samma fråga. Notera att jag inte har Visual Studio installerat på den här maskinen så jag har bara knackat ihop det lite snabbt i notepad.
[PHP]var applicationUsers = (from u in _context.Users.AsNoTracking()
join cu in _context.Users.AsNoTracking() on u.SymtomId equals cu.SymtomId)
where u.StatusId == (byte)AvailableStatus.Available &&
cu.Id == userId).ToList();[/PHP]
Man ska vara sparsam med .ToList(), .FirstOrDefault(), .SingleOrDefault() m.m. i EF.
Citera
2016-11-02, 13:37
  #11
Medlem
Sane?s avatar
Citat:
Ursprungligen postat av tj.
Varför skulle det inte finnas ett enkelt sätt p.g.a. det? Något sådant här bör fungera om man vill ha det i samma fråga. Notera att jag inte har Visual Studio installerat på den här maskinen så jag har bara knackat ihop det lite snabbt i notepad.
[PHP]var applicationUsers = (from u in _context.Users.AsNoTracking()
join cu in _context.Users.AsNoTracking() on u.SymtomId equals cu.SymtomId)
where u.StatusId == (byte)AvailableStatus.Available &&
cu.Id == userId).ToList();[/PHP]
Man ska vara sparsam med .ToList(), .FirstOrDefault(), .SingleOrDefault() m.m. i EF.
Jobbigt att läsa alla svar?
Citera
2016-11-02, 14:17
  #12
Medlem
Sane?s avatar
Citat:
Ursprungligen postat av Trillske
Minitips är att inte använda Var när du hämtar instans via linq, man ser redan här i svaren att så fort någon bryter din notation (instans som lowercase av typen) blir det dåligt, och den kan vara svår att upprätthålla överallt.
Eftersom man av prestandaskäl normalt hämtar ut en anonym typ med exakt det man vill ha och inte hela objektgrafer så är var vanligast.
Citera
  • 1
  • 2

Skapa ett konto eller logga in för att kommentera

Du måste vara medlem för att kunna kommentera

Skapa ett konto

Det är enkelt att registrera ett nytt konto

Bli medlem

Logga in

Har du redan ett konto? Logga in här

Logga in