高性能ORM框架XLinq功能详细介绍
之前简单介绍了XLinq的一些功能,有很多功能都没有提到,现在给XLinq加了一些功能,这次把所有功能都介绍一遍。
设计目标
- 易用性
在使用一个框架的时候
应该没几个人会喜欢写一大堆的配置文件吧
也应该没几个人会喜欢为了完成一个小功能却需要写一大堆代码
这是XLinq开发的首要目标之一,就是尽可能提高易用性
最小配置的情况下仅需要一句连接字符串的配置就可以使用
默认支持的是Sql Server 2008 R2数据库,理论上说也大部分支持了sql server系的其他数据库
- 高性能
目前针对查询时的DataReader转List和批量插入有针对性优化,其他的不太重要的例如批量更新这个并没有太多优化
- 易于调试
出了问题能够以最快速度让使用者定位到问题,其实这些是细节问题
- 支持LINQ
这个其实是跟易用性挂勾的,园子里面有大神写的ORM,声称"无Linq",其实我也不太想用Linq,因为解析Linq实在太难,坑太多,但又找不到另一种比Linq更让我满意的方案。然后我去看了他的ORM的使用方法,发现一旦复杂起来不见得会比Linq更清晰,并且实体类的设计实在是···
Linq实现复杂的语法确实比较蛋疼,sql语句里面直接来几个case when,linq就得写晕过去,至少解析的时候会晕过去。
但在我看来,既然linq语句都比较复杂了,那肯定是有很多的逻辑在里面,就像上面说的case when,那么为什么不把这些逻辑分拆成好几个linq去执行呢?或者干脆点写存储过程里面。
- 多数据库支持
使用方法
- 建立控制台应用程序,暂时只支持.net 4.5.2版本
- 安装xlinq nuget包
- 建立数据库XLinq及相关表
- 添加配置文件
- <connectionStrings>
- <add name="test" connectionString="Data Source=(local);;initial catalog=XLinq;User Id=xinchen;Password=123456"/>
- </connectionStrings>
- 建立TestDataContext类和User实体类
- public class TestDataContext:DataContext
- {
- public TestDataContext():base("test")
- {
- }
- }
- public class User
- {
- public int Id { get; set; }
- public string Username { get; set; }
- }
- 开始使用
- TestDataContext db = new TestDataContext();
- db.Set<User>().ToList();
查询
- 单表查询
Lambda版:
- db.Set<User>().Where(x => x.Username == "xxxx").ToList();
LINQ版:
- (from user in db.Set<User>() where user.Username == "xxxx" select user).ToList();
LINQ版看起来明显感觉比较麻烦,所以在简单查询的时候我更倾向于Lambda表达式
上面的语句生成的代码是一致的
- [Users1].[Id] [Id]
- ,[Users1].[Username] [Username]
- 多表查询
建立实体类的过程不再说
两个表连接查询,因为用Lambda实现比较复杂,所以后面都不再用Lambda实现
- var query = from user in db.Set<User>()
- join userRole in db.Set<UserRole>() on user.Id equals userRole.UserId
- where user.Id == 1 && userRole.RoleId == 1
- select new
- {
- user.Id,
- userRole.RoleId,
- user.Username
- };
生成的语句
五个表连接查询
- var query = from user in db.Set<User>()
- join userRole in db.Set<UserRole>() on user.Id equals userRole.UserId
- join rolePrivilege in db.Set<RolePrivilege>() on userRole.RoleId equals rolePrivilege.RoleId
- join priviege in db.Set<Privilege>() on rolePrivilege.PrivilegeId equals priviege.Id
- join role in db.Set<Role>() on userRole.RoleId equals role.Id
- where user.Id == 1 && userRole.RoleId == 1
- select new
- {
- user.Id,
- userRole.RoleId,
- user.Username,
- PrivilegeName = priviege.Name,
- RoleName = role.Name
- };
生成的语句
- 左连接查询
因为linq本身的左连接写法比较蛋疼,所以xlinq也没有办法,后面会想办法简化
- var query = from user in db.Set<User>()
- join userRole in db.Set<UserRole>() on user.Id equals userRole.UserId into urs
- from ur in urs.DefaultIfEmpty()
- where user.Id == 1 && ur.RoleId == 1
- select new
- {
- user.Id,
- ur.RoleId,
- user.Username
- };
生成的语句
- 延迟加载
- var query = db.Set<User>().Where(x => x.Username == "xxxxx").Select(x => x.Id);
- foreach (var item in query)
- {
- }
- 自连接查询
- var query = from user1 in db.Set<User>()
- join user2 in db.Set<User>() on user1.Id equals user2.Id
- select user1;
生成的语句
- SQL语句查询
- db.SqlQuery<User>("select * from dbo.users", new Dictionary<string, object>());
- 分页查询
- var page = 1;
- var pageSize = 10;
- var query = (from user in db.Set<User>()
- join userRole in db.Set<UserRole>() on user.Id equals userRole.UserId
- join rolePrivilege in db.Set<RolePrivilege>() on userRole.RoleId equals rolePrivilege.RoleId
- join priviege in db.Set<Privilege>() on rolePrivilege.PrivilegeId equals priviege.Id
- join role in db.Set<Role>() on userRole.RoleId equals role.Id
- where user.Id == 1 && userRole.RoleId == 1
- orderby user.Id descending
- select new
- {
- user.Id,
- userRole.RoleId,
- user.Username,
- PrivilegeName = priviege.Name,
- RoleName = role.Name
- }).Skip((page - 1) * pageSize).Take(pageSize);
生成的语句
- 动态查询
- IQueryable<User> query = db.Set<User>();
- var filters = new List<SqlFilter>();
- filters.Add(SqlFilter.Create("Id", Operation.Equal, 1));
- filters.Add(SqlFilter.Create("Username", Operation.Like, "aaa"));
- query = query.Where(filters);
生成的语句
- [Users1].[Id] [Id]
- ,[Users1].[Username] [Username]
- 取日期查询
这个功能主要针对EF中无法直接取日期的问题
- var query = db.Set<User>().Where(x => x.LoginTime.Date == DateTime.Now.Date);
- [Users1].[Id] [Id]
- ,[Users1].[Username] [Username]
- ,[Users1].[LoginTime] [LoginTime]
- 计算天数查询
- var query = db.Set<User>().Where(x => (x.LoginTime - DateTime.Now).TotalDays <= 7);
生成的语句
- [Users1].[Id] [Id]
- ,[Users1].[Username] [Username]
- ,[Users1].[LoginTime] [LoginTime]
修改
- 先查询后修改(注意只有十条数据以内才会支持修改)
- var query = db.Set<User>().FirstOrDefault();
- query.Username = "xxxxxxx";
- db.SaveChanges();
- 直接修改
- db.Set<User>().Where(x => x.Id == 1).Update(x => new User
- {
- Username = "xxxxxxxxxxxxx"
- });
Update子句必须采用这写法才会有效
删除
- 先查询后删除
- var query = db.Set<User>().FirstOrDefault();
- db.Set<User>().Remove(query);
- db.SaveChanges();
- 直接删除
- db.Set<User>().Where(x => x.Id == 1).Delete();
事务
- 普通事务
- using (var scope = new TransactionScope())
- {
- db.Set<User>().Where(x => x.Id == 1).Delete();
- scope.Complete();
- }
- 嵌套事务
- using (var scope = new TransactionScope())
- {
- db.Set<User>().Where(x => x.Id == 1).Delete();
- using (var s1 = new TransactionScope())
- {
- db.Set<Privilege>().Where(x => x.Id == 1).Delete();
- s1.Complete();
- }
- scope.Complete();
- }
调试支持
在调试的时候可直接看到SQL语句
多数据库支持
通过配置文件实现多数据库支持
- <configSections>
- <section name="xlinq" type="Xinchen.XLinq.ConfigSection,Xinchen.XLinq"/>
- </configSections>
- <xlinq connectionStringName="test" dataBase="SQLite"></xlinq>
链式Api
有没有园友注意到,我上面用的User实体并没有进行任何特殊处理,但实际翻译出来的语句是识别的Users表
这其实是"抄"的EF的约定大于配置的原则,默认情况下,实体名的复数就是表名,实体中的Id识别为主键并自动增长
如果需要自定义这些规则,则需要使用特性或链式API,我现在更喜欢链式API
- public class TestDataContext:DataContext
- {
- public TestDataContext():base("test")
- {
- }
- protected override void ConfigurationModel(Xinchen.XLinq.Configuation.EntityConfigurationManager entityConfiguration)
- {
- entityConfiguration.Entity<User>().TableName("Users").Key(x => x.Id, Xinchen.DbUtils.DataAnnotations.DataSourceTypes.AutoIncreament);
- }
- }
重写DataContext的ConfigurationModel方法即可使用链式API对实体相关信息进行配置,上面是指定了User实体的表名、主键及主键的数据来源为自动增长
创建表
若要创建表,则几乎必须使用链式API对实体进行配置之后才能进行自动创建
另外这个功能有一定的限制,默认情况下不会启用该功能,若启用了也只有在数据库中一个表都没有的情况下才会自动创建表
这样做是因为真正的数据库运行环境其实很可能压根不允许自动创建表,然后就是下面这个样子,这样一来这个功能其实并没有太大用,只有在第一次初始化数据库的时候才会用。
当然也可以始终自动创建表,这个需要稍微配置一下,当allwayAutoCreateTables为true时并且autoCreateTables为true时,将始终自动创建表
下面演示创建一个Test表
DataContext中的配置
- public class TestDataContext : DataContext
- {
- public TestDataContext()
- : base("test")
- {
- }
- protected override void ConfigurationModel(Xinchen.XLinq.Configuation.EntityConfigurationManager entityConfiguration)
- {
- var testEntity = entityConfiguration.Entity<Test>().TableName("TestTables").Key(x => x.Id, Xinchen.DbUtils.DataAnnotations.DataSourceTypes.AutoIncreament);
- testEntity.Property(x => x.Id).Name("TId");
- testEntity.Property(x => x.Name).MaxLength(100);
- }
- }
创建的表
自定义Provider(有这功能,但没有测试)
性能
- 查询
- class Program
- {
- static int count = 1;
- static int rowCount = 1000000;
- static void Main(string[] args)
- {
- TestDataContext xlinqDb = new TestDataContext();
- TestDbContext efDb = GetEF(null);
- var userDB = xlinqDb.Set<User>();
- var userRoleDb = xlinqDb.Set<UserRole>();
- var efUserDb = efDb.Users;
- var efUserRoleDb = efDb.UserRoles;
- ExecuteTimer("XLinq使用Linq", () =>
- {
- for (int i = 0; i < count; i++)
- {
- userDB.Take(rowCount).ToList();
- }
- });
- ExecuteTimer("XLinq使用SQL", () =>
- {
- var sql = "select top " + rowCount + " * from dbo.users";
- var dict = new Dictionary<string, object>();
- for (int i = 0; i < count; i++)
- {
- xlinqDb.SqlQuery<User>(sql, dict);
- }
- });
- ExecuteTimer("XLinq多表", () =>
- {
- for (int i = 0; i < count; i++)
- {
- (from user in userDB
- join userRole in userRoleDb on user.Id equals userRole.UserId into us
- from u in us.DefaultIfEmpty()
- select user).Take(rowCount).ToList();
- }
- });
- efDb = GetEF(efDb);
- efUserDb = efDb.Users;
- efUserRoleDb = efDb.UserRoles;
- ExecuteTimer("EF使用LINQ", () =>
- {
- for (int i = 0; i < count; i++)
- {
- efUserDb.Take(rowCount).ToList();
- }
- });
- efDb = GetEF(efDb);
- efUserDb = efDb.Users;
- efUserRoleDb = efDb.UserRoles;
- ExecuteTimer("EF使用SQL", () =>
- {
- var sql = "select top " + rowCount + " * from dbo.users";
- for (int i = 0; i < count; i++)
- {
- efDb.Database.SqlQuery<User>(sql).ToList();
- }
- });
- efDb = GetEF(efDb);
- efUserDb = efDb.Users;
- efUserRoleDb = efDb.UserRoles;
- ExecuteTimer("EF多表", () =>
- {
- for (int i = 0; i < count; i++)
- {
- (from user in efUserDb
- join userRole in efUserRoleDb on user.Id equals userRole.UserId into us
- from u in us.DefaultIfEmpty()
- select user).Take(rowCount).ToList();
- }
- });
- SqlConnection conn = new SqlConnection();
- conn.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings[1].ConnectionString;
- ExecuteTimer("Dapper", () =>
- {
- var sql = "select top " + rowCount + " * from dbo.users";
- for (int i = 0; i < count; i++)
- {
- conn.Query<User>(sql);
- }
- });
- Console.ReadKey();
- }
- static void ExecuteTimer(string name, Action action)
- {
- var watcher = Stopwatch.StartNew();
- action();
- watcher.Stop();
- Console.WriteLine(string.Format("{0}查询{1}数据{2}次", name, rowCount, count).PadRight(30) + ":" + watcher.Elapsed);
- }
- static TestDbContext GetEF(TestDbContext ef)
- {
- if (ef != null)
- {
- ef.Dispose();
- }
- TestDbContext efDb = new TestDbContext();
- efDb.Users.Where(x => false).ToList();
- efDb.Configuration.AutoDetectChangesEnabled = false;
- efDb.Configuration.ProxyCreationEnabled = false;
- efDb.Configuration.ValidateOnSaveEnabled = false;
- return efDb;
- }
- public class TestDbContext : DbContext
- {
- public TestDbContext()
- : base("name=test")
- {
- }
- public System.Data.Entity.DbSet<User> Users { get; set; }
- public System.Data.Entity.DbSet<UserRole> UserRoles { get; set; }
- }
- }
测试结果
查询一次的情况下EF慢的妥妥的,XLinq微弱优势
查询N次的情况下EF这···还有人说EF慢?不过大概是因为缓存的原因,但最后一张图看着又不太像,或许还跟GC什么的关吧,所以还有人说EF慢么··
或者是我测试代码有问题?
- 插入
- class Program
- {
- static int count = 10;
- static int rowCount = 1000;
- static void Main(string[] args)
- {
- TestDataContext xlinqDb = new TestDataContext();
- TestDbContext efDb = GetEF(null);
- var userDB = xlinqDb.Set<User>();
- var userRoleDb = xlinqDb.Set<UserRole>();
- var efUserDb = efDb.Users;
- var efUserRoleDb = efDb.UserRoles;
- ExecuteInsertTimer("XLinq", () =>
- {
- for (int i = 0; i < rowCount; i++)
- {
- userDB.Add(new User()
- {
- C1 = "x",
- C2 = "x",
- C3 = "x",
- C4 = "x",
- C5 = "x",
- C6 = "x",
- C7 = "x",
- C8 = "x",
- C9 = "x",
- C10 = "x",
- Username = "xxxx"
- });
- }
- xlinqDb.SaveChanges();
- });
- ExecuteInsertTimer("EF", () =>
- {
- for (int i = 0; i < rowCount; i++)
- {
- efUserDb.Add(new User()
- {
- C1 = "x",
- C2 = "x",
- C3 = "x",
- C4 = "x",
- C5 = "x",
- C6 = "x",
- C7 = "x",
- C8 = "x",
- C9 = "x",
- C10 = "x",
- Username = "xxxx"
- });
- }
- efDb.SaveChanges();
- });
- Console.ReadKey();
- }
- static void ExecuteTimer(string name, Action action)
- {
- var watcher = Stopwatch.StartNew();
- action();
- watcher.Stop();
- Console.WriteLine(string.Format("{0}查询{1}数据{2}次", name, rowCount, count).PadRight(30) + ":" + watcher.Elapsed);
- }
- static void ExecuteInsertTimer(string name, Action action)
- {
- var watcher = Stopwatch.StartNew();
- action();
- watcher.Stop();
- Console.WriteLine(string.Format("{0}插入{1}条数据", name, rowCount).PadRight(30) + ":" + watcher.Elapsed);
- }
- static TestDbContext GetEF(TestDbContext ef)
- {
- if (ef != null)
- {
- ef.Dispose();
- }
- TestDbContext efDb = new TestDbContext();
- efDb.Users.Where(x => false).ToList();
- efDb.Configuration.AutoDetectChangesEnabled = false;
- efDb.Configuration.ProxyCreationEnabled = false;
- efDb.Configuration.ValidateOnSaveEnabled = false;
- return efDb;
- }
- public class TestDbContext : DbContext
- {
- public TestDbContext()
- : base("name=test")
- {
- }
- public System.Data.Entity.DbSet<User> Users { get; set; }
- public System.Data.Entity.DbSet<UserRole> UserRoles { get; set; }
- }
- }
测试结果
测试源码下载
http://files.cnblogs.com/files/wzxinchen/XlinqDemo.zip
高性能ORM框架XLinq功能详细介绍的更多相关文章
- 高性能ORM 框架之 MySqlSugar
mysql 3.X API地址: http://www.cnblogs.com/sunkaixuan/p/5987308.html MySqlSugar 1.5 API 一.介简 SqlSugar ...
- Java Annotation认知(包括框架图、详细介绍、示例说明)
摘要 Java Annotation是JDK5.0引入的一种注释机制. 网上很多关于Java Annotation的文章,看得人眼花缭乱.Java Annotation本来很简单的,结果说的人没说清楚 ...
- Java Annotation认知(包括框架图、详细介绍、示例说明)(转)
本文转自:http://www.cnblogs.com/skywang12345/p/3344137.html 网上很多关于Java Annotation的文章,看得人眼花缭乱.Java Annota ...
- Ubuntu根目录下各文件夹的功能详细介绍
Ubuntu的根目录下存在着很多的文件夹,但你知道他们都存放着哪些文件呢?这些是深入了解Ubuntu系统必不缺少的知识,本文就关于此做一下介绍吧. /bin/ 用以存储二进制可执行命令文件. / ...
- Asp.Net 高性能ORM框架 SqlSugar.ORM 2.8
3.0最新API: http://www.cnblogs.com/sunkaixuan/p/5911334.html 1.前言/Preface SqlSugar从去年到现在已经一年了,版本从1.0升到 ...
- Asp.Net 高性能ORM框架——SqlSugar
公司团队项目.产品已经完全抛弃EF,SqlSugar定位不是ORM,而是为了方便的让你去写Sql. SqlSugar 媲美原生ADO.NET的性能,语法简洁,并且支持 Json .Dynamic. L ...
- 抓包工具 - HttpWatch(功能详细介绍)
HttpWatch是功能强大的网页数据分析工具,集成在IE工具栏,主要功能有网页摘要.cookies管理.缓存管理.消息头发送/接收,字符查询.POST数据.目录管理功能和报告输出.HttpWatch ...
- HttpWatch功能详细介绍
来源:https://www.cnblogs.com/Chilam007/p/6947235.html HttpWatch是功能强大的网页数据分析工具,集成在IE工具栏,主要功能有网页摘要.cooki ...
- ThinkPHP表单令牌验证功能详细介绍
注:TP版本为3.1.3 在ThinkPHP框架下,两次提交同一个表单,比如提交信息后在浏览器点击后退退回上次的页面,重新点击提交按钮,就会提示“表单令牌错误”的信息. ThinkPHP新版内置了表单 ...
随机推荐
- pythond对象、异常、反射的学习笔记
python多继承,刚开始我是表示惊讶的,毕竟学的php,哪来的多继承?顶多也就是利用接口模拟多继承后者使用反射机制实现.那么还是来看看python的强大吧 1 首先,Python的类继承了多个类,那 ...
- centos 下 apache 重启启动命令
apache 启动 usr/local/apache243/bin/apachectl start apache 重启 usr/local/apache243/bin/apachectl restar ...
- C语言+ODBC+SQL 操作(向SQL里面添加数据)
为了节省时间,我就引用上一节的数据库的表和C语言的结构体数组,在结构体数组中添加数据,清空数据库数据. 第一步查询:SQLBindParameter函数的用法. SQLRETURN SQLBindPa ...
- 插入排序(C语言)
输入一个数,插入到已排序的队列中 第一:定义一个已经排好的整型数组 如: int arry[7]={2,3,5,11,15,17}; 或输入一串整型的数组,再排序(冒泡.选择都可以) 下面我们用冒泡 ...
- 导入表 IMPORT_DESCRIPTOR
typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null i ...
- NUnit+mock+moq单元测试
[TestFixture] public class InstantBatchBuyTest { private string _mallAbc; private string _itemCode; ...
- eclipse中格式化代码快捷键Ctrl+Shift+F失效的解决办法
很长一段时间我的eclipse都有个毛病,就是当我要格式化代码的时候,右键-source-format能够起效,但ctrl+shift+f不好使了. 可以基本断定是快捷键与别的软件冲突了,但一直也没时 ...
- AsyncTask和Handler的优缺点比较
AsyncTask实现的原理和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口 ...
- Android ListView嵌套Button,Button事件覆盖item事件解决办法
方法就是修改item布局的xml文件: 在根布局里加上: android:descendantFocusability="blocksDescendants" 然后在按钮布局里加上 ...
- 公告:本博客搬迁到:http://www.courtiercai.com/
公告: 您好,本人意见本博客搬迁到:http://www.courtiercai.com/.