AutoMapper 转换时处理字段 asp.net core

public class AutoMapperConfig : Profile
    {
        public AutoMapperConfig()
        {
            CreateMap<Models.Goods, Models.V_Goods>().ForMember(
                x => x.AddTime,
                opt => opt.MapFrom(src => ((DateTime)src.AddTime).ToString("yyyy-MM-dd HH:mm:ss")));
        }
    }

Models.Goods转化为Models.V_Goods时,将Goods.AddTime转换为格式化后的字符串

asp.net core mvc 404错误

遇到这个错误,一定不要慌,第一、看下路由是否正确,如果刚刚还能打开,并且没有改动路由信息,现在就404那就不是路由的问题。第二、如果确认路由没动,这时候最恶心了,一定要看下依赖注入是不是没有注册?在 Startup.cs文件中,public void ConfigureServices(IServiceCollection services){},浏览器怎么都是404,调试vs不报错,不报错…耽误我几个小时…,第三看有没有写错,如果 写成了 private readonly Logger _logger ;而不是private readonly ILogger _logger ;也会一样的404报错,vs不报错,所以一定要注意,传入的必须是Interface,写错一个字母就404了

asp.net core 接收 payload application/json 参数

asp.net core 接收 payload application/json 参数,payload有点特殊,不像普通的get或者post那样key、value对应,而application/json只是给服务器返回一个json数据,所以不能类似a=1&b=2,用string a,string b接收,两种办法处理这个问题
1、用模型
比如json,他本身是一个数组,那么像对应的用List可以接收如下内容:

[
    {
        "ContentId": 4,
        "ContentCategoryId": 0,
        "Title": "他在网吧苦练多年 最终代表中国赢下WCG世界冠军1",
        "Sort": 100,
        "AddTime": "2018-11-21T14:57:58.64062",
        "UpdateTime": "2018-11-21T02:56:38",
        "Hits": 0,
        "ContentCategory": null
    },
    {
        "ContentId": 13,
        "ContentCategoryId": 0,
        "Title": "他在网吧苦练多年 最终代表中国赢下WCG世界冠军2",
        "Sort": 100,
        "AddTime": "2018-11-21T14:57:58.64062",
        "UpdateTime": "2018-11-21T02:56:38",
        "Hits": 0,
        "ContentCategory": null
    }]
public string Sort([FromBody]List<Models.V_Content> list)
        {
            return "";
        }

2、用object 或者dynamic
我在开发中遇到必须要传一个对象,而不能是数组,比如vue的axios,

axios.post('/admin/Content/sort',
                    {"list":this.list},
                )

json:

{
    "list": [
        {
            "ContentId": 4,
            "ContentCategoryId": 0,
            "Title": "他在网吧苦练多年 最终代表中国赢下WCG世界冠军1",
            "Sort": 100,
            "AddTime": "2018-11-21T14:57:58.64062",
            "UpdateTime": "2018-11-21T02:56:38",
            "Hits": 0,
            "ContentCategory": null
        },
        {
            "ContentId": 13,
            "ContentCategoryId": 0,
            "Title": "他在网吧苦练多年 最终代表中国赢下WCG世界冠军2",
            "Sort": 100,
            "AddTime": "2018-11-21T14:57:58.64062",
            "UpdateTime": "2018-11-21T02:56:38",
            "Hits": 0,
            "ContentCategory": null
        }]
}

那么此时就用用object 或者dynamic

[HttpPost]
        public string Sort([FromBody]object json)
        {
            JObject jObject = JObject.Parse(json.ToString());
            var  data = jObject.SelectToken("list").ToString();
            var list = JsonConvert.DeserializeObject<List<Models.V_Content>>(data);
            .....
            return "";
        }

记一次惨痛的EF报错 When attaching existing entities, ensure that only one entity instance with a given key value is attached

InvalidOperationException: The instance of entity type ‘Category’ cannot be tracked because another instance with the same key value for {‘CategoryID’} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using ‘DbContextOptionsBuilder.EnableSensitiveDataLogging’ to see the conflicting key values.
翻译过来的内容大致是:实体 category不能被跟踪,因为有一个同样key categoryID已经被跟踪。
一、情况描述
一个新项目,用了ASP.net Core MVC开发,ORM使用EF,ASP.net Core 默认使用了依赖注入的概念。很简单的一个功能,就是一个层级的类别管理,在列表、添加页面都没有出现问题,唯独在更新的时候出现了问题,项目使用了两层的仓库结构,使用了默认的依赖注入,使用了unitOfwork,项目结构如下:

二、出现此问题的原因:大致理解为ef跟踪的内容已经不是从初始化上下文跟踪的内容
三、网上查到的解决办法,
1、使用AsNoTracking()不进行跟踪,但是这种方法不能对数据进行更新,只能查询,但是我这里需要更新。
2、更新之前重新查询一次数据然后更新,这个显然有点蛋疼。

四、错误排除方法:
1、是否造成多次实例化context,查询和更新的上下文不是一个,经过对context构造函数的日志输出,确认每次请求只实例化1个context。
2、调试程序,观察当前model的EntityState状态,发现是Detached,Detached的解释:对象存在,但未由对象服务跟踪。在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态,我用了微软asp.net core mvc 示例同样调试状态发现也是Detached,所以这个可以排除。
3、由于用了两层的仓储(BLL,DAL),剥开每一层进行测试,跳过BLL使用DAL测试,跳过BLL,DAL,直接使用context,发现都没有问题,那么问题可能出在BLL层。对BLL的方法一个个测试,发现只要不执行GetTree()方法,问题就不会出现。
这个方法的目的是将类别树取出来,进行排序用于显示,第二个方法做一个递归,有一个全局变量_tree保存处理结果,通过Category._tree调用。

public List _tree = new List();
public void GetTree()
        {
            var list = _repository.GetList().ToList();            
            GetTree(list, 0, 0);
            
        }
public void GetTree(List<Models.Category> list, int parentID, int level)
        {
            var tempList = list.Where(a => a.ParentID == parentID).OrderBy(a => a.Sort);
            if (tempList.Count() != 0)
            {
                foreach (var item in tempList)
                {
                    item.Level = level;
                    _tree.Add(item);
                    GetTree(list, item.CategoryID, level + 1);
                }
            }
        }

发现两个问题
第一、一旦var list=_repository.GetList().ToList()就不能更新时候报错,我理解是_repository是引用类型,ToList之后,ef里的实体就会被Tolist,ef的跟踪状态发生了变化。
第二、不用Tolist,改成IQueryable,往下面走,foreach (var item in tempList){},这个方法改成一个空方法依然更新时候报错,我理解,在foreach的时候,IQueryable已经不在表达式了,已经是从数据库里取出了数据,ef的跟踪状态发生了变化,所以报错。

那么GetTree()是什么时候执行的呢,这又是一个错误,为了每次方便取出_tree,我把GetTree(),写到了控制器的构造函数里,造成不管是查询,更新都会先执行一次GetTree()。

五、总结
正常更新的时候是前台传递过来model,后台获取context,更新model到context,这个期间不要动context,不要tolist和foreach操作,虽然EntityState也是Detached,但是不能进行更新。
这个问题差不多一整天都在解决,多么痛的领悟。

2019-04-10

public JsonResult Edit(Models.Attr attr)
        {
            var dataModel = _attr.Find(attr.AttrId);
            if (dataModel.AttrName != attr.AttrName)
            {
                var count= _attr.Count(a => a.AttrName == attr.AttrName);
                if (count > 0)
                {
                    var msg = new Models.PageMsg(false, "属性名重复");
                    return Json(msg);
                }
            }
            _attr.Update(attr, true);
            return Json(new Models.PageMsg(true, "操作成功", "/admin/attr/"));
           
        }

上面代码同样的提示,改为以下代码

public JsonResult Edit(Models.Attr attr)
        {
            var dataModel = _attr.Find(attr.AttrId);
            if (dataModel.AttrName != attr.AttrName)
            {
                var count= _attr.Count(a => a.AttrName == attr.AttrName);
                if (count > 0)
                {
                    var msg = new Models.PageMsg(false, "属性名重复");
                    return Json(msg);
                }
            }
            dataModel.AttrName = attr.AttrName;
            dataModel.AttrType = attr.AttrType;
            dataModel.DefaultValue = attr.DefaultValue;
            _attr.Update(dataModel, true);
            return Json(new Models.PageMsg(true, "操作成功", "/admin/attr/"));
        }

Detached:对象存在,但未由对象服务跟踪。在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态;
Unchanged:自对象加载到上下文中后,或自上次调用 System.Data.Objects.ObjectContext.SaveChanges() 方法后,此对象尚未经过修改;
Added:对象已添加到对象上下文,但尚未调用 System.Data.Objects.ObjectContext.SaveChanges() 方法;
Deleted:使用 System.Data.Objects.ObjectContext.DeleteObject(System.Object) 方法从对象上下文中删除了对象;
Modified:对象已更改,但尚未调用 System.Data.Objects.ObjectContext.SaveChanges() 方法。

加载了名为“Microsoft.VisualStudio.Web.PageInspector.Tracing”的程序集。使用此上下文会导致序列化、强制转换和依赖项解析出现意外的行为

vs报错如下:

托管调试助手 “LoadFromContext”:“使用 LoadFrom 上下文从“file:///C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/Common7/IDE/Extensions/Microsoft/Web Tools/Page Inspector/Microsoft.VisualStudio.Web.PageInspector.Tracing.DLL”加载了名为“Microsoft.VisualStudio.Web.PageInspector.Tracing”的程序集。使用此上下文会导致序列化、强制转换和依赖项解析出现意外的行为。建议用户在任何情况下都避免使用 LoadFrom 上下文。这可以通过在全局程序集缓存或在 ApplicationBase 目录中安装程序集,并在显式加载程序集时使用 Assembly.Load 来实现。”

解决方法:

打开C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config、web.config),将这个内容的“add”改为“remove”:

<compilation>
<assemblies>
<remove assembly=”Microsoft.VisualStudio.Web.PageInspector.Loader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a” />

找到多个与名为“user”的控制器匹配的类型 两个Area 解决办法

项目架构:两个Area 一个 Admin,一个User,两个Area里面都有重名的控制器UserControl,结果报错

通过搜索,网站的一致解决办法为,在路由里面用namespaces限定 如下

在区域Member Admin和最外层的RouteConfig.cs限定

public override void RegisterArea(AreaRegistrationContext context) 
        {
            context.MapRoute(
                "Member_default",
                "Member/{controller}/{action}/{id}",
                new { Controller = "Home", action = "index", id = UrlParameter.Optional },
                 namespaces: new string[] {"SSHB.Areas.Member.Controllers"}
            );
        }
public override void RegisterArea(AreaRegistrationContext context) 
        {
            context.MapRoute(
                "Admin_default",
                "Admin/{controller}/{action}/{id}",
                new { Controller = "Home", action = "index", id = UrlParameter.Optional },
                namespaces: new string[] {"SSHB.Areas.Admin.Controllers"}
            );
        }
public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                namespaces: new string[] { "SSHB.WEB.Controllers" }
            );
        }

结果Admin/User/index、User/User/Index正常,/AAA/这种不存在的控制器返回404也正常,但是当访问到/User/index/的时候仍然出现上述错误,花了2个多小时找问题,最后发现必须要在RouteConfig.cs里添加一个参数才可以DataTokens[“UseNamespaceFallback”] = false

最终结果:

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                namespaces:new string[] {"SSHB.WEB.Controllers"}
            ).DataTokens["UseNamespaceFallback"] = false; 
        }

至此全部正常,有点坑爹,关于DataTokens[“UseNamespaceFallback”] ,默认为true,设置为false,则只在当前设置的命名空间内匹配规则,而不会去到其他的命名空间里查找匹配。

坑爹的地方在于如果不设置DataTokens[“UseNamespaceFallback”] =false,他会去所有的命名空间下查找User,最终在Area下查找到重名的两个控制器,然后报错,不匹配了还报什么错,直接返回404就完事,日

echarts宽度 100px的问题 % px

在使用echarts的时候发现echarts的宽度变成了100px,明明设置的是100%,一般出现这种情况是在tab切换或者bootstrap的模态框里面,原因是当tab或者modal在隐藏的时候,无法获取到他的宽度值,echarts默认100px的宽度,我用的是bootstrap的modal,解决办法:

第一步、使用modal的shown.bs.modal方法获取弹出框的宽度。

官方文档:当模态框对用户来说可见时(需要等待CSS过渡完成),会触发该事件。如果是由点击引起的,被点击的元素是可用的,成为Event对象的relatedTarget属性。

第二步、使用echarts的resize方法,重新设置大小。

 $('#tree').on('shown.bs.modal', function () {
        myChart.resize({ width: $("#echarts").width(), height: 500 });
    })

echarts的数据模型生成 C# 用户组织架构图

用户上下级关系、层级关系全部循环到一个Model里面去

public class UserController : ApiController
    {
        DAL.EF db = new DAL.EF();
        
        //上级结果只有一个 tree[0],用一个tree[0]存储当前用户以及所有下级的关系
        List<Models.User> tree = new List<Models.User>();

        //用一个V存储所有下级的echarts模型
        V_model V = new V_model();

        public string GetTreeJson(int id)
        {
            var list = db.User_Repository.dbSet.SqlQuery(@"WITH TREE AS(SELECT * FROM [User] WHERE userid = " + id + " UNION ALL SELECT [User].* FROM [User], TREE WHERE [User].ParentID = TREE.userid )SELECT* FROM TREE ").ToList();
            if (list.Count == 0)
            {
                return "";
            }
            //初始值
            var first = list[0];
            V.name = first.TrueName;
            V.value = first.UserNo;
            V.children = new List<V_model>();
            tree.Add(first);

            //开始递归
            AddSub(list,first,V);
            //递归完成
            return JsonConvert.SerializeObject(V) ;//输出echart 数据对象
            //return JsonConvert.SerializeObject(list) ;//输出Models.User对象
        }
        public void AddSub(List<Models.User> all, Models.User curUser,V_model vModel)
        {
            List<Models.User> sublist = all.Where(a => a.ParentID == curUser.UserID).ToList(); //得到子节点
            curUser.SubUsers = sublist;
            foreach (var subItem in sublist)
            {
                var vv = new V_model {
                    name = subItem.TrueName,
                    value = subItem.UserNo,
                    children = new List<V_model>()
                     };
                vModel.children.Add(vv);
                AddSub(all, subItem,vv);
            }
        }
    }
//Echarts数据对象模型
    public class V_model
    {
        public string name { get; set; }
        public string value { get; set; }
        public List<V_model> children { get; set; }
    }
public partial class User
    {
        [Key]
        public int UserID { get; set; }
        ......
        public int ParentID{get;set;}
        [NotMapped]
        public virtual List<Models.User> SubUsers { get; set; }
    }