ASP.NET MVC и хранимые процедуры работают некорректно

Так что у меня, скорее всего, что-то не так, я это уже знаю. Я просто не могу понять, что именно не так. Я пробовал это двумя разными способами, и каждый из них получил разные результаты. Итак, я пытаюсь использовать хранимые процедуры для получения данных для представления. У меня есть две модели просмотра, которые таковы:

public class CharacterCraftNamesListViewModel
    {
        public string CharFullName { get; set; }
        public string ProfName { get; set; }
    }

и

public class CharacterCraftCraftListViewModel
    {
        public string CraftClassName { get; set; }
        public int CharCraftCharID { get; set; }
        public int CharCraftClassID { get; set; }
        public int CharCraftLevelID { get; set; }
        public bool CraftLevelSet { get; set; }
        public string CraftLevelName { get; set; }
        public bool CraftLevelMastery { get; set; }

    }

У меня также есть две соответствующие хранимые процедуры в базе данных.

CREATE PROCEDURE [dbo].[GetCharacterCraftCharacterNameProfessionName]
                @CharID int = NULL
    AS
    WITH CHCRNames_CTE ( [CCCID], [CharFull], [ProfName] )
    AS
        (SELECT 
            Character_Char_ID,
            CASE 
                    WHEN b.Char_Last_Name IS NULL THEN b.Char_First_Name
                    ELSE b.Char_First_Name + '  ' + b.Char_Last_Name
            END AS FullName,
            c.Profession_Name
            FROM CharacterCraft a LEFT OUTER JOIN
            [Character] b ON a.Character_Char_ID = b.Char_ID LEFT OUTER JOIN
            [Profession] c ON c.Profession_ID = b.Profession_Profession_ID
            )
    SELECT DISTINCT CharFull, ProfName
    FROM CHCRNames_CTE
    WHERE CCCID = @CharID

и

CREATE PROCEDURE [dbo].[GetCharacterCraftRank]
                @CharID int = NULL,
                @Rank int = NULL
    AS  
    WITH CHCR_CTE ( [Rank], [CCID], [CCCCID], [CCName], [CLCLID], [CLName], [CLTier], [CLS], [CLM])
    AS
        (SELECT
                DENSE_RANK() OVER(PARTITION BY(a.Character_Char_ID)ORDER BY (a.CraftClass_Craft_Class_ID)) AS [Rank],
                a.Character_Char_ID,
                CraftClass_Craft_Class_ID,
                c.Craft_Class_Name,
                CraftLevel_Craft_Level_ID,
                d.Craft_Level_Name,
                d.Craft_Level_Tier,
                Craft_Level_Set,
                Craft_Level_Mastery
        FROM CharacterCraft a LEFT OUTER JOIN
        [Character] b ON a.Character_Char_ID = b.Char_ID LEFT OUTER JOIN
        [CraftClass] c ON a.CraftClass_Craft_Class_ID = c.Craft_Class_ID LEFT OUTER JOIN
        [CraftLevel] d ON a.CraftLevel_Craft_Level_ID = d.Craft_Level_ID
        )
    SELECT  [CCID], [CCCCID], [CCName], [CLCLID], [CLS], [CLName], [CLM] 
    FROM CHCR_CTE 
    WHERE [CCID]= @CharID AND [Rank] = @Rank
    ORDER BY [Rank], [CLTier]

Внутри моего контроллера у меня есть следующее:

public async Task<ActionResult> Edit(int? id)
            {
                if (id == null)
                {
                    return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
                } 
    var names = await db.Database.SqlQuery<CharacterCraftNamesListViewModel>( sql: "GetCharacterCraftCharacterNameProfessionName", parameters: new object[] { id } ).ToListAsync();
            var alist = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>( sql: "GetCharacterCraftRank", parameters: new object[] { id, 1 } ).ToListAsync();
            var blist = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>( sql: "GetCharacterCraftRank", parameters: new object[] { id, 2 }  ).ToListAsync();
            var clist = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>( sql: "GetCharacterCraftRank", parameters: new object[] { id, 3 }  ).ToListAsync();
            var characterCraft = new CharacterCraftViewModel()
            {
                CharNames = names.AsEnumerable(),
                CraftListA = alist.AsEnumerable(),
                CraftListB = blist.AsEnumerable(),
                CraftListC = clist.AsEnumerable()
            };
    if (characterCraft == null)
        {
            return HttpNotFound();
        }
        return View(characterCraft);
    }

Когда я смотрю в отладчик, я вижу следующее:

  • id 1
  • количество имен = 0
  • alist Count = 0
  • количество блистов = 0
  • clist Count = 0
  • characterCraft
    {LotroMvc.Models.CharacterCraftViewModels.CharacterCraftViewModel}

Таким образом, я получаю пустую страницу.

Теперь я попытался разместить хранимые процедуры в самом контроллере и получил другой результат в отладчике.

Внутри контроллера я попробовал:

public async Task<ActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
var query = "WITH CHCRNames_CTE( [CCCID], [CharFull], [ProfName] ) "
                    + "AS "
                    + "( SELECT "
                    + "Character_Char_ID, "
                    + "CASE "
                    + "WHEN b.Char_Last_Name IS NULL THEN b.Char_First_Name "
                    + "ELSE b.Char_First_Name + '  ' + b.Char_Last_Name "
                    + "END AS FullName, "
                    + "c.Profession_Name "
                    + "FROM CharacterCraft a LEFT OUTER JOIN "
                    + "dbo.[Character] b ON a.Character_Char_ID = b.Char_ID LEFT OUTER JOIN "
                    + "dbo.[Profession] c ON c.Profession_ID = b.Profession_Profession_ID "
                    + ") "
                    + "SELECT DISTINCT CharFull, ProfName "
                    + "FROM CHCRNames_CTE "
                    + "WHERE CCCID = @p0";
        var names = await db.Database.SqlQuery<CharacterCraftNamesListViewModel>( query, id ).ToListAsync();
        var rank = "WITH CHCR_CTE( [Rank], [CCID], [CCCCID], [CCName], [CLCLID], [CLName], [CLTier], [CLS], [CLM] )"
                    + "AS "
                    + "( SELECT "
                    + "DENSE_RANK() OVER(PARTITION BY(a.Character_Char_ID)ORDER BY (a.CraftClass_Craft_Class_ID)) AS [Rank], "
                    + "a.Character_Char_ID, "
                    + "CraftClass_Craft_Class_ID, "
                    + "c.Craft_Class_Name, "
                    + "CraftLevel_Craft_Level_ID, "
                    + "d.Craft_Level_Name, "
                    + "d.Craft_Level_Tier, "
                    + "Craft_Level_Set, "
                    + "Craft_Level_Mastery "
                    + "FROM CharacterCraft a LEFT OUTER JOIN "
                    + "[Character] b ON a.Character_Char_ID = b.Char_ID LEFT OUTER JOIN "
                    + "[CraftClass] c ON a.CraftClass_Craft_Class_ID = c.Craft_Class_ID LEFT OUTER JOIN "
                    + "[CraftLevel] d ON a.CraftLevel_Craft_Level_ID = d.Craft_Level_ID "
                    + ") "
                    + "SELECT  [CCID], [CCCCID], [CCName], [CLCLID], [CLS], [CLName], [CLM] "
                    + "FROM CHCR_CTE "
                    + "WHERE [CCID]= @p0 AND [Rank] = @p1 "
                    + "ORDER BY [Rank], [CLTier]";
        var alist = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>( rank, parameters: new object[] { id, 1 } ).ToListAsync();
        var blist = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>( rank, parameters: new object[] { id, 2 }  ).ToListAsync();
        var clist = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>( rank, parameters: new object[] { id, 3 }  ).ToListAsync();
        var characterCraft = new CharacterCraftViewModel()
        {
            CharNames = names.AsEnumerable(),
            CraftListA = alist.AsEnumerable(),
            CraftListB = blist.AsEnumerable(),
            CraftListC = clist.AsEnumerable()
        };
if (characterCraft == null)
    {
        return HttpNotFound();
    }
    return View(characterCraft);
}

Это дает мне следующее в отладчике:

  • это {LotroMvc.Controllers.CharacterCraftsController}
  • id 1
  • запрос "WITH CHCRNames_CTE ([CCCID], [CharFull], [ProfName]]" AS (SELECT Character_Char_ID, CASE WHEN b.Char_Last_Name IS NULL THEN
    b.Char_First_Name ELSE b.Char_First_Name + '' + b.Char_Last_Name
    END AS FullName, c.Profession_Name FROM CharacterCraft a LEFT OUTER
    JOIN dbo. [Character] b ON a.Character_Char_ID = b.Char_ID LEFT OUTER JOIN dbo. [Profession] c ON c.Profession_ID =
    b.Profession_Profession_ID ) ВЫБЕРИТЕ DISTINCT CharFull, ProfName FROM CHCRNames_CTE, ГДЕ CCCID = @ p0 "names Count = 1
  • [0] {LotroMvc.Models.CharacterCraftViewModels.CharacterCraftNamesListViewModel}
  • CharFullName null
  • ProfName "Историк"
  • ранг "WITH CHCR_CTE ([Rank], [CCID], [CCCCID], [CCName], [CLCLID], [CLName], [CLTier], [CLS], [CLM]" AS (SELECT DENSE_RANK () OVER (PARTITION ПО (a.Character_Char_ID) ORDER BY (a.CraftClass_Craft_Class_ID)) AS [место], a.Character_Char_ID, CraftClass_Craft_Class_ID, c.Craft_Class_Name, CraftLevel_Craft_Level_ID, d.Craft_Level_Name, d.Craft_Level_Tier, Craft_Level_Set, Craft_Level_Mastery ОТ CharacterCraft левое внешнее соединение [Характер ] b ВКЛ a.Character_Char_ID = b.Char_ID ЛЕВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ [CraftClass] c ВКЛЮЧЕНО a.CraftClass_Craft_Class_ID = c.Craft_Class_ID ЛЕВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ [CraftLevel] d ВКЛ a.CraftLevel_Craft_Level_ID], [CC_ID_Craft_ID], [CC_Level_ID]) [CCName], [CLCLID], [CLS], [CLName], [CLM] FROM CHCR_CTE WHERE [CCID] = @ p0 AND [Rank] = @ p1 ORDER BY [Rank], [CLTier] »
  • alist Count = 9
  • [0] {LotroMvc.Models.CharacterCraftViewModels.CharacterCraftCraftListViewModel}
  • CharCraftCharID 0
  • CharCraftClassID 0
  • CharCraftLevelID 0
  • CraftClassName null
  • CraftLevelMaster false
  • CraftLevelName null
  • CraftLevelSet false
  • (и так далее)

Хотя данные в списке определенно неверны, их количество верное. Имена отображают правильное ProfName, но неправильные данные в CharFullName. Так что я не понимаю, что здесь делать. Если я выполняю хранимые процедуры в T-SQL, я получаю правильные данные, но только на сервере. Мне не удалось правильно воспроизвести MVC и SQL, и я знаю, что это мой код. Я просто не вижу, что не так с кодом. Есть мысли, где я ошибся?


person Mike Mastro    schedule 01.01.2017    source источник


Ответы (1)


Хорошо, я понял это. Проблема была вызвана двумя ошибками. Со следующим кодом программа неправильно вызывала его на SQL Server:

var names = await db.Database.SqlQuery<CharacterCraftNamesListViewModel>( sql: "GetCharacterCraftCharacterNameProfessionName", parameters: new object[] { id } ).ToListAsync();
        var alist = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>( sql: "GetCharacterCraftRank", parameters: new object[] { id, 1 } ).ToListAsync();
        var blist = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>( sql: "GetCharacterCraftRank", parameters: new object[] { id, 2 }  ).ToListAsync();
        var clist = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>( sql: "GetCharacterCraftRank", parameters: new object[] { id, 3 }  ).ToListAsync();

Хотя вызов SQLQuery верен во всем, что я прочитал, когда я поместил в SSMS то, что было выведено программой, я получил нулевые наборы. Поэтому я изменил sql: «GetCharacterNameProfessionName» и sql: «GetCharacterCraftRank» на sql: «GetCharacterNameProfessionName @ p0» и sql: «GetCharacterCraftRank @ p0, @ p1». Затем я получаю результат, похожий на тот, когда я писал запросы в контроллере.

Следующая проблема сводилась к соглашению об именах. Я был очень сбит с толку, когда я создал модель Database First Model, чтобы посмотреть, что произойдет, и это действительно сработало. Но в процессе работы я понял, что могу сопоставить имена столбцов хранимой процедуры со столбцами, которые были у меня в коде. Именно тогда меня осенило, как простое изменение моих хранимых процедур, чтобы имена столбцов совпадали с тем, что я вставлял в программу, и все работало правильно. Несколько небольших настроек, и теперь мой контроллер get выглядит так:

public async Task<ActionResult> Edit(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        var characterCraft = new CharacterCraftViewModel()
        {
            CharNames = await db.Database.SqlQuery<CharacterCraftNamesListViewModel>(sql: "GetCharacterCraftCharacterNameProfessionName @p0", parameters: new object[] { id }).FirstAsync(),
            CraftListA = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>(sql: "GetCharacterCraftRank @p0, @p1", parameters: new object[] { id, 1 }).ToListAsync(),
            CraftListB = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>(sql: "GetCharacterCraftRank @p0, @p1", parameters: new object[] { id, 2 }).ToListAsync(),
            CraftListC = await db.Database.SqlQuery<CharacterCraftCraftListViewModel>(sql: "GetCharacterCraftRank @p0, @p1", parameters: new object[] { id, 3 }).ToListAsync()
        };

        if (characterCraft == null)
        {
            return HttpNotFound();
        }
        return View(characterCraft);
    }

Я надеюсь, что это поможет кому-то другому.

person Mike Mastro    schedule 21.01.2017