文章出處

最近一直用ASP.NET MVC 4.0 +LINQ TO SQL來開發設計公司內部多個業務系統網站,在這其中發現了一些問題,也花了不少時間來查找相關資料或請教高人,最終都還算解決了,現在我將這些問題及對應的解決方案都整理匯總出來,供大家參供,有不對之處或有更好的解決辦法,歡迎在本文評論,謝謝!

【2014-12-2發布】

問題一:執行類似語句:dbDataContext.TableName.Join(modelList as List<實體對象類型>,t1=>t1.id,t2=>t2.id,(t1,t2)=>new{屬性賦值}),報錯:不能在查詢運算符(Contains 運算符除外)的 LINQ to SQL 實現中使用本地序列。

原因分析:數據表映射實體對象無法與C#自有集合對象關聯查詢,必需確保LINQ 語句進行查詢與運算均為數據表映射實體對象或C#自有集合對象

解決方案:dbDataContext.TableName.Join(dbDataContext.TableName2,t1=>t1.id,t2=>t2.id,(t1,t2)=>new{屬性賦值}),或dbDataContext.TableName.AsEnumerable().Join(modelList as List<實體對象類型>,t1=>t1.id,t2=>t2.id,(t1,t2)=>new{屬性賦值}),但后者存在性能問題,因為AsEnumerable()就會立即執行查詢動作,將TableName中所有的數據加載到本地內存中后才去與后面的modelList 關聯。

 

問題二:執行類似語句:dbDataContext.TableName.Select(t=>new 數據表映射實體類{屬性賦值}),報錯:不允許在查詢中顯式構造實體類型“XXXXXXXXX”。

原因分析:摘自網絡上的原話“LINQ to SQL在RTM之前的版本有個Bug,如果在查詢中顯式構造一個實體的話,在某些情況下會得到一系列完全相同的對象。很可惜這個Bug我只在資料中看到過,而在RTM版本的LINQ to SQL中這個Bug已經被修補了,確切地說是繞過了。直接拋出異常不失為一種“解決問題”的辦法,雖然這實際上是去除了一個功能——沒有功能自然不會有Bug,就像沒有頭就不會頭痛了一個道理。”

解決方案:1.將Select(t=>new 數據表映射實體類{屬性賦值})改為直接返回匿名類:Select(t=>new {屬性賦值}),或重新定義該實體類對象,去掉與數據表映射相關的特性,即:Select(t=>new 自定義實體類{屬性賦值}),2.利用LINQ to SQL中DataContext提供有GetCommand方法,擴展方法ExecuteQuery<T>,代碼如下:

public static class DataContextExtensions
{
    public static List<T> ExecuteQuery<T>(this DataContext dataContext, IQueryable query)
    {
        DbCommand command = dataContext.GetCommand(query);
        dataContext.OpenConnection();
 
        using (DbDataReader reader = command.ExecuteReader())
        {
            return dataContext.Translate<T>(reader).ToList();
        }
    }
 
    private static void OpenConnection(this DataContext dataContext)
    {
        if (dataContext.Connection.State == ConnectionState.Closed)
        {
            dataContext.Connection.Open();
        }
    }
}

在執行的時候就可以先以LINQ查詢,然后執行ExecuteQuery方法,如:

var query=dbDataContext.TableName.Select();
var modelList=dbDataContext.ExecuteQuery<數據表映射實體類>(query);

問題三:使用ModelState.AddModelError(“字段名”,“錯誤信息”)添加多個信息時,在VIEW中用Html.ValidationSummary(false) 顯示的報錯順序不一定與AddModelError的先后順序相同,即:

ModelState.AddModelError(“字段名1”,“錯誤信息1”);
ModelState.AddModelError(“字段名9”,“錯誤信息2”);
ModelState.AddModelError(“字段名6”,“錯誤信息3”);
ModelState.AddModelError(“字段名3”,“錯誤信息4”);
ModelState.AddModelError(“字段名5”,“錯誤信息5”);

顯示出來可能是(無序的或以字段名排序后顯示):

錯誤信息1
錯誤信息4
錯誤信息5
錯誤信息3
錯誤信息9

這就明顯會影響用戶體驗,所以建議使用以下方法,這樣顯示出來的錯誤就是正常的,原理很簡單,因為若ModelState.AddModelError為同一個鍵,此處為空,則會在該鍵的ModelState.Errors下添加項,而由于Errors最終存為List類型,所以索引順序也就確定了

ModelState.AddModelError(“”,“錯誤信息1”);
ModelState.AddModelError(“”,“錯誤信息2”);
ModelState.AddModelError(“”,“錯誤信息3”);
ModelState.AddModelError(“”,“錯誤信息4”);
ModelState.AddModelError(“”,“錯誤信息5”);

 

問題四:將匿名對象作為Model數據傳給View并顯示時,報錯:“object”不包含“XXX”的定義。

原因分析:匿名類型默認訪問修飾符為internal,這意味著他們只可以從其定義的程序集中被訪問。一旦你超越了程序集的邊界,將會被當做普通的object對象被解析,因此不具備直接索引屬性。

解決方案:1.使用Tuple元組靜態類,即:

Controller中:
var
result= dbDataContext.TableName.Select(s=>Tuple.Create(參數賦值)); View中使用: @model IEnumerable<dynamic> foreach (var item in Model) { <tr> <td>@item.Item1</td> <td>@item.Item2</td> <td>@item.Item3</td> <td>@item.Item4</td> <td>@item.Item5</td> </tr> }

2.還可以使用ExpandoObject類,這是.NET 4.0中的一種類型:ExpandoObject,ExpandoObject類型是一種可以再運行時隨意動態添加和刪除成員的類型。


Controller中:
public ActionResult UsingExpando()  
{  
    dynamic viewModel = new ExpandoObject();  
    viewModel.TestString = "This is a test string";  
   
    return View(viewModel);  
}  

View中使用:

<p> @Model.TestString </p>

 

【2014-12-09發布】

問題五:從視圖頁面接收到MODEL對象后(POST到ACTION),對MODEL對象各屬性進行變更并重新傳給視圖顯示,但顯示的結果仍然是之前視圖上編輯的MODEL的值

        public ActionResult EditUser(string id)
        {
            T_SysUser user;
            if (string.IsNullOrEmpty(id))
            {
                user = new T_SysUser();
                user.enabled = true;
            }
            else
            {
                user = asotsDb.T_SysUser.Where(u => u.userid == id).SingleOrDefault();
                if (user == null)
                {
                    throw new Exception("無效的網頁地址參數!");
                }
            }

            ViewBag.IsNew = string.IsNullOrEmpty(id);

            return View(user);
        }

        [HttpPost]
        public ActionResult EditUser(string id, T_SysUser model)
        {
            try
            {
                if (string.IsNullOrEmpty(model.userid))
                {
                    ModelState.AddModelError("userid", "用戶ID不能為空!");
                }

                if (string.IsNullOrEmpty(id) && string.IsNullOrEmpty(model.password))
                {
                    ModelState.AddModelError("password", "密碼不能為空!");
                }
                else if (!string.IsNullOrEmpty(model.password) && model.password.Length < 6)
                {
                    ModelState.AddModelError("password","密碼字符串長度必需>=6位!");
                }

                if (string.IsNullOrEmpty(model.employeeid))
                {
                    ModelState.AddModelError("employeeid", "工號不能為空!");
                }

                if (string.IsNullOrEmpty(model.realname))
                {
                    ModelState.AddModelError("realname", "姓名不能為空!");
                }

                if (ModelState.IsValid)
                {
                    var loginedUserInfo = UserBusiness.GetLoginedUserInfo();
                    T_SysUser user = asotsDb.T_SysUser.Where(u => u.userid == model.userid).SingleOrDefault();
                    if (string.IsNullOrEmpty(id))
                    {
                        if (user != null)
                        {
                            ModelState.AddModelError("userid", "該用戶ID已經存在!");
                        }
                        else
                        {
                            model.password = Common.GetMD5(model.password);
                            model.lasteupdateby = loginedUserInfo["realname"];
                            model.lasteupdatebyid = loginedUserInfo["userid"];
                            model.lastupdatedatetime = DateTime.Now;
                            asotsDb.T_SysUser.InsertOnSubmit(model);
                        }
                    }
                    else
                    {
                        string oldPassword = user.password;
                        UpdateModel(user);
                        user.password = string.IsNullOrEmpty(model.password) ? oldPassword : Common.GetMD5(model.password);
                        user.lasteupdateby = loginedUserInfo["realname"];
                        user.lasteupdatebyid = loginedUserInfo["userid"];
                        user.lastupdatedatetime = DateTime.Now;
                    }

                    if (ModelState.IsValid)
                    {
                        asotsDb.SubmitChanges();
                        ViewBag.SuccessMsg = "保存成功!";
                        if (string.IsNullOrEmpty(id))
                        {
                            model = new T_SysUser();
                        }
                    }
                }

            }
            catch (Exception ex)
            {
                ModelState.AddModelError("", ex.Message);
            }

            ViewBag.IsNew = string.IsNullOrEmpty(id);

            return View(model);

        }
View Code
@model ASOTS.Models.T_SysUser
           
@{
    ViewBag.Title = "EditUser";

}
@using (Html.BeginForm())
{
<fieldset>
    <legend>編輯用戶信息</legend>
    <table class="edittable">
        <tr>
            <td class="datatile">用戶ID:</td>
            <td>
                @if (ViewBag.IsNew)
                {
                    @Html.TextBoxFor(m => m.userid, new { @class = "input" })
                }
                else
                {
                     @Html.TextBoxFor(m => m.userid, new { @class = "input", @readonly = "readonly" })
                }
                @Html.ValidationMessage("userid")
            </td>
            <td class="datatile">密 碼:</td>
            <td>@Html.PasswordFor(m => m.password, new { @class = "input" })
                @Html.ValidationMessage("password")
            </td>
        </tr>
        <tr>
            <td class="datatile">工 號:</td>
            <td>@Html.TextBoxFor(m => m.employeeid, new { @class = "input" })
                @Html.ValidationMessage("employeeid")
            </td>
            <td class="datatile">姓 名:</td>
            <td>@Html.TextBoxFor(m => m.realname, new { @class = "input" })
                 @Html.ValidationMessage("realname")
            </td>
        </tr>
        <tr>
            <td class="datatile">職 位:</td>
            <td>@Html.TextBoxFor(m => m.position, new { @class = "input" })</td>
            <td class="datatile">聯系電話:</td>
            <td>@Html.TextBoxFor(m => m.telno, new { @class = "input" })</td>
        </tr>
        <tr>
            <td class="datatile">可用否:</td>
            <td>@Html.DropDownListFor(m => m.enabled, new[]{new SelectListItem(){Text="可用",Value="True"},
                                new SelectListItem(){Text="禁用",Value="False"}}, new { @class = "input" })</td>
            <td></td>
            <td></td>
        </tr>
    </table>
    <p class="btns">
        <input type="submit" value="保 存" class="button" />
    </p>
</fieldset>

}

@if (ViewBag.SuccessMsg != null)
{
<div class="success_box">
    @ViewBag.SuccessMsg
</div>
}

@Html.ValidationSummary(true)
View Code

原因分析:初步認為是當ModelState中有鍵值時,在使用View(model)或ViewData.Model=model時,系統自動會將ModelState與model對象進行映射并更新,造成了不論你如何變更model對象的各屬性值,最終顯示到視圖時,仍是顯示上次視圖界面的值,有點類似webform中的viewstate,當然這個只是我的猜測,若哪位高手知道其原理還望告之。

解決方案:在重新創建model對象后,1.若要清除model對象中某個屬性的值,則可以使用:ModelState.Remove("Model屬性名稱"),

2.若要清除model對象所有屬性的值,則可以使用:ModelState.Clear()

 

問題六:使用LINQ TO SQL附加MODEL對象并執行更新或刪除時(如:dbDataContext.T_SysUser.Attach(user); dbDataContext.SubmitChanges();),報錯:已嘗試Attach或Add實體,該實體不是新實體,可能是從其他DataContext中加載來的。不支持這種操作。

 原因分析:詳見使用LINQ to SQL更新數據庫(上):問題重重

解決方案:詳見使用LINQ to SQL更新數據庫(中):幾種解決方案,我一直采用的是重新賦值,如下:

T_SysUser user = asotsDb.T_SysUser.Where(u => u.userid == model.userid).SingleOrDefault();
UpdateModel(user);

 

【2014-12-17】 

問題七:當使用JQuery AJAX POST數組到ACTION時,ACTION無法直接接收該數組信息

原因分析:用JQuery.Ajax 提交Array的數據,提交的時候始終會在名稱后面加上”[]”

解決方案:1.若是普通的數組,比如:字符串數組、數字數組,則可使用如下方法:

        [HttpPost]
        public ActionResult Test(FormCollection form)
        {
             string[] userRoles = form.GetValues("params[]");//這里就可以直接獲取AJAX POST過來的數組
            
            return View();
        }

2.若是復雜類型或對象數組,則可使用如下方法:

[HttpPost]
public ActionResult Test(FormCollection form)
{
    var models=form.GetValues("params[]").Select(p=>p.Single<Model>()).ToArray();
}

或自定義繼承DefaultModelBinder的一個類,并重寫BindModel方法:

    public class JQAjaxModelBinder : DefaultModelBinder
    {

        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType.IsArray) //判斷是否為數組
            {
                var key = bindingContext.ModelName + "[]";
                var valueResult = bindingContext.ValueProvider.GetValue(key);
                if (valueResult != null && !string.IsNullOrEmpty(valueResult.AttemptedValue))
                {
                    bindingContext.ModelName = key;
                }
            }
            return base.BindModel(controllerContext, bindingContext);
        }
    }

在ACTION中可以如下使用:

        [HttpPost]
        public ActionResult Test([ModelBinder(typeof(JQAjaxModelBinder))] Model[] models)
        {
             Model model=models[0]; //這里舉例獲取數組中某個對象
             model.name="zuowenjun";
             model.sex=1;
             model.url="www.zuowenjun.cn";
            return View();
        }

 

【2015-03-06】

 問題七:ObjectStateManager 中已存在具有同一鍵的對象。ObjectStateManager 無法跟蹤具有相同鍵的多個對象。

原因分析:對象的實體狀態已處于了 deattach狀態;

解決方案:查詢的時候加上asNoTracking(),然后在進行更新或刪除時,使用Attach即可;

 

后續若還有新的問題,會持續更新,工作中學習,學習中總結,總結后實踐,實踐后掌握!




 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()