关于EF的导航属性

假如有三个实体,不相关的属性不显示

1、发票

    [Table("TShopInvoice")]
    public class ShopInvoiceModel
    {
        public int Id { get; set; }
        public int OrderId { get; set; }
        public int UserId { get; set; }
        public virtual UsersModel UsersModel { get; set; }
        public virtual ShopOrderModel ShopOrderModel { get; set; }
    }

2、订单

    [Table("TShopOrder")]
    public class ShopOrderModel
    {
        [Key]
        public int id { get; set; }
        public int userid { get; set; }
        /// <summary>
        /// <summary>
        /// 订单项集合
        /// </summary>
        public virtual List<ShopOrderGoodsModel> GoodsItem { get; set; }
        public virtual UsersModel UsersModel { get; set; }
        public virtual ICollection<ShopInvoiceModel> ShopInvoiceModel { get; set; }
    }

3、客户

    [Table("TUser")]
    public class UsersModel
    {
        [Key]
        public int Id { get; set; }
        public virtual ICollection<ShopOrderModel> ShopOrderModel { get; set; }
        public virtual ICollection<ShopInvoiceModel> ShopInvoiceModel { get; set; }
    }

先搞清楚关系:

一个订单可能会有几张发票,但是一个发票只能对应一个订单

一个客户可能会有几张发票,但是一个发票只可能开给一个客户

配置一对多的关系(一对多与多对一的配置,应该是一样的吧?):

ModelBuilder.Entity<ShopOrderModel>().HasMany(e => e.ShopInvoiceModel).WithRequired(e => e.ShopOrderModel).HasForeignKey(e => e.OrderId).WillCascadeOnDelete(false);
ModelBuilder.Entity<UsersModel>().HasMany(e => e.ShopInvoiceModel).WithRequired(e => e.UsersModel).HasForeignKey(e => e.UserId).WillCascadeOnDelete(false);

要达到可以通过点号点出相关对象,我们在查询的时候必须加载相关数据。EF加载数据的方式,有预加载(Eager Loading)、延迟加载(Lazy Loading)、显式加载(Explicit Loading)。不同的加载方式都有不同的适用情况,我们不能在这里笼统地下决定说哪种方式好哪种方式不好。但有一点是需要遵循的,那就是如何提高数据加载的效率。此外,还有人提到按需加载的概念,其实那只是一种自创的方式,并不官方的方式。

延迟加载。在下面的代码中,首先会执行一次查询,并将返回的结果存放在变量customers 中。而foreach循环会在每一次循环过程中独立进行一次查询,所以,如果数据库表Customer中有100条记录,那么整个过程将产生101次查询。

using (var dbcontext=  new ModelFirstDemoEntities())
{
            #region 延迟加载:用的时候加载
            var customers = from c in dbcontext.Customer
                            select c;

            foreach (var cus in customers)
            {
                Console.WriteLine(cus.Id);
                foreach (var order in cus.Order)  //根据导航属性,自动查询客户的订单
                {
                    Console.WriteLine(order.OrderContent);
                }
            }
            #endregion
}

预加载。如果你想让所有数据一次性全部加载到内存中,那么你需要使用.Include(Entity)方法。

using (var dbcontext=  new ModelFirstDemoEntities())
{
            #region 贪婪加载: 一次查询加载全部数据
            var q = from t in dbcontext.Customer.Include("Order")
                    select t;

            foreach (var cus in q)
            {
                Console.WriteLine("Teacher : {0}", cus.Id);
                Console.WriteLine("Respective Courses...");
                foreach (var order in cus.Order)
                {
                    Console.WriteLine("Course name : {0}", order.OrderContent);
                }
                Console.WriteLine();
            }
            #endregion

}

只有一次数据交互过程,即程序只通过一次查询便获取到了所有需要的数据。它也可以减少程序与数据库的交互次数。不过仍然有缺点,那就是如果数据量较大,一次性将所有数据载入内存往往并不是最明智的选择。.Include(Entity)方法允许级联使用,你可以预先加载具有多层级结构的数据。

显式加载。显式加载和延迟加载非常类似,不同的是显式加载要手动关闭EF的延迟加载属性,通过代码ctx.Configuration.LazyLoadingEnabled = false;来完成。

using (var dbcontext= new ModelFirstDemoEntities())
{
    dbcontext.Configuration.LazyLoadingEnabled = false;
            #region 显式加载:查询部分列数据,前提关闭 懒加载
            //查询表中部分列的数据
            var items = from c in dbcontext.Customer
                        select c;
            foreach (var item in items)
            {
                //条件判断,只加载满足条件的数据,减少访问数据库的次数
                if (item.Id < 5)
                {
                    dbcontext.Entry(item).Collection(c => c.Order).Load();
                    Console.WriteLine(item.CusName);
                }

                foreach (var order in item.Order)
                {
                    Console.WriteLine("Course name : {0}", order.OrderContent);
                }
            }
            #endregion
}

按需加载。其实EF并不存在按需加载的概念,但是这种方式很值得说一说,在加载数据的时候并不是需要所有的属性值,可能只需要一个Id,Name值。所以我们可以对要查询的实体进行一下筛选,只加载自己需要的某些列,避免加载大量的垃圾数据。在这里按需加载的概念只是加载需要的列。可能会与前端开发中 的概念“按需加载”有所冲突。

#region 按需加载:查询部分列数据
//查询表中部分列的数据
var items = from c in dbcontext.Customer
	where c.Id < 10
	select new { Id = c.Id, CName = c.CusName, OrderCount = c.Order.Count() };
foreach (var item in items)
{
Console.WriteLine(item.CName);
}
#endregion

回到文章开头的问题:

public static List<ShopInvoiceModel> GetByFilter(DateTime start, DateTime end, int pageIndex, int pageSize, ref int count)
{
    using (NMProjectContext db = new NMProjectContext())
    {
	//以下第一行是预加载模式,不用为好,所数据量太大
	//IQueryable<ShopInvoiceModel> query = from tb in db.ShopInvoiceModel.Include("ShopOrderModel").Include("UsersModel") where tb.CreateTime >= start && tb.CreateTime <= end select tb;
	IQueryable<ShopInvoiceModel> query = from tb in db.ShopInvoiceModel where tb.CreateTime >= start && tb.CreateTime <= end select tb;
	query = query.OrderByDescending(a => a.Id);
	count = query.Count();
	//以下两行不行,DBContext.Entry(object)只接受单一对象,不接受List或者IQueryable<Entry>
	//db.Entry(query).Reference("ShopOrderModel").Load();
	//db.Entry(query).Reference("UsersModel").Load();
	List<ShopInvoiceModel> r = query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
	r.ForEach(p => db.Entry(p).Reference("ShopOrderModel").Load());
	//或者如下方式
	//r.ForEach(p => db.Entry(p).Reference(x=>x.ShopOrderModel).Load());
	r.ForEach(p => db.Entry(p).Reference("UsersModel").Load());
	// 如果要从单一对象(比如用户)找出其相关的所有发票,就用:
	//context.Entry(userobject).Collection(p => p.ShopInvoiceModel).Load();
	//context.Entry(userobject).Collection("ShopInvoiceModel").Load();
	return r;
    }
}

如果使用sqlquery则是如下方式:

System.Data.SqlClient.SqlParameter[] param = { whereP, totalRecordP, totalPageP, pageSizeP, pageIndexP, tableNameP, orderFieldP, AscOrDescP };
var results = db.Database.SqlQuery<ShopInvoiceModel>("exec dbo.GetList  @where, @totalRecord OUTPUT, @TotalPage OUTPUT, @pageSize, @pageIndex,@tablename,@orderField,@AscOrDesc", param);
List<ShopInvoiceModel> list = results.ToList();
list.ForEach(p => db.ShopInvoiceModel.Attach(p));
list.ForEach(p => db.Entry(p).Reference(x => x.ShopOrderModel).Load());
list.ForEach(p => db.Entry(p).Reference(x => x.UsersModel).Load());
count = Convert.ToInt32(totalRecordP.Value);
totalPage = Convert.ToInt32(totalPageP.Value);
return list;

Using Query to count related entities without loading them.Sometimes it is useful to know how many entities are related to another entity in the database without actually incurring the cost of loading all those entities. The Query method with the LINQ Count method can be used to do this. For example:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    // Count how many posts the blog has  
    var postCount = context.Entry(blog)
                           .Collection(b => b.Posts)
                           .Query()
                           .Count();
}

 

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值