深度优先

这个家伙好懒,除了文章什么都没留下

0%

对于程序员来说,编程过程中或多或少会和数据库打交道。如果采用Visual Studio进行程序开发,则微软的Sql Server数据库是最好的选择。但是问题来了,Sql Server数据库动辄几个G,安装后占用的空间也相当大,是不是每个开发人员在开发时都需要安装Sql Server呢?其实,对于小型项目、测试型项目、学习型项目的开发,完全没必要使用Sql Server那么高大上的数据库。微软自己也深知这点,因此,推出了Sql Server数据库的超级简化版本:Sql Server LocalDB。这个小型的数据库完全可以满足普通项目的开发和调试,关键是它只有几十M,可以大大减轻PC的运行压力。本文将简要介绍在Visual Studio 2015中LocalDB数据库的使用方法。

一、LocalDB的安装

在安装VS2015时会自动安装LocalDB,所以只要正确安装VS2015那么localDB是肯定有的。

二、LocalDB的连接和管理

进入VS2015,在“视图”中选择“Sql Server对象资源管理器”,可以看到如下的界面。

右键单击Sql Server,可以选择“添加Sql Server”。

在弹出的窗体中浏览本地,可以看见有两个数据库实例:MSSQLLocalDB和ProjectsV13。这两个实例都是VS2015的自带LocalDB,之所以有两个,是因为楼主的VS2015进行过更新,MSSQLLocalDB是属于V12版本的LocalDB,ProjectsV13是属于V13版本的LocalDB,二者目前都可以被VS2015的工程使用。接下来以MSSQLLocalDB为例进行连接。成功连接后左侧的资源管理器就可以对数据库的资源进行管理。

然后我们可以尝试创建一个新的数据库Test。

成功创建Test数据库后,可以正常地数据库进行操作。然而,如果要连接外部的数据库文件,就无法直接在Sql Server资源管理器中进行操作,此时需要在服务器资源管理器中进行操作。过程如下:

在服务器资源管理器中选择添加连接,如果直接连接外部数据库文件,则该文件会默认附加到LocalDB中:

在弹出的窗体中选择“浏览”来添加外部数据库文件:

选中文件后点击“确定”,服务器资源管理器中就多了一个外部数据库文件的连接:

再次查看Sql Server对象资源管理器中的MSSQLLocalDB,可以看见多了刚才添加的外部数据库被附加到了这个本地数据库中:

至此,外部数据库也附加完毕,如果要在项目中连接该数据库,连接字符串如下:

“Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=外部数据库文件的绝对路径(注意转义字符)”。

命令行操作:

At the command line, you can interact using the program name SqlLocalDb. Start with getting information about the installation:

1
C:\> SqlLocalDb info 

Result:

1
MSSQLLocalDB 

This used to return the version number (in the original article, and with the 2012 release, this returned v11.0.

Next, you can create an instance with the following command:

1
C:\> SqlLocalDb create "MyInstance" 

Result:

1
LocalDB instance "MyInstance" created with version 14.0.3030.27. 

Check the info:

1
C:\> SqlLocalDb info "MyInstance" 

Result:

1
Name: MyInstance Version: 14.0.3030.27 Shared name: Owner: PEDRO\aaronbertrand Auto-create: No State: Running Last start time: 7/20/18 10:44:51 AM Instance pipe name: np:\\.\pipe\LOCALDB#9EBB1CD2\tsql\query 

The Instance pipe name may come in handy later, though I’ve found that a lot of the connectivity issues in earlier versions of this feature have gone away. Also, in older versions you had to explicitly start the instance, but it now starts automatically.

If you want to stop and drop the instance, use:

1
C:\> SqlLocalDb stop "MyInstance" C:\> SqlLocalDb delete "MyInstance" 

But don’t do that just yet. Evidence that this all works so far:

Command line interaction with LocalDB

Connect using SQLCMD

Locate SQLCMD, making sure to use the newest version on your machine (your environment path may list an older version first). Look for the highest version in the Binn folders under C:\Program Files\Microsoft SQL Server\Client SDK\ODBC[version]\Tools. You can connect to this instance using the following from the command line:

1
sqlcmd -S "(localdb)\MyInstance" 

Then we’re greeted with a line number prompt and can enter commands at will. So something like:

1
SELECT @@VERSION; GO CREATE DATABASE blat; GO USE blat; GO CREATE TABLE dbo.splunge(Mort int); GO INSERT dbo.Splune(Mort) VALUES(1); SELECT * FROM dbo.splunge; GO 

Yields:
SQLCMD interaction with LocalDB

相关内容转自:
http://www.cnblogs.com/Traveller-Lee/archive/2016/08/16/5776312.html

https://www.mssqltips.com/sqlservertip/5612/getting-started-with-sql-server-2017-express-localdb/

一、介绍

Quartz.Net是根据Java的Quartz用C#改写而来,最新的版本是3.0.6,源码在https://github.com/quartznet/quartznet。主要作用是做一些周期性的工作,或者定时工作。比如每天凌晨2点对前一天的数据统计。

二、简单的案例

以WebApi项目举例,用VS脚手架功能新建WebApi项目。

1
2
3
4
5
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();//注册ISchedulerFactory的实例。
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly ISchedulerFactory _schedulerFactory;
private IScheduler _scheduler;
public ValuesController(ISchedulerFactory schedulerFactory)
{
this._schedulerFactory = schedulerFactory;
}
[HttpGet]
public async Task<string[]> Get()
{
       //1、通过调度工厂获得调度器
_scheduler = await _schedulerFactory.GetScheduler();
       //2、开启调度器
await _scheduler.Start();
       //3、创建一个触发器
var trigger = TriggerBuilder.Create()
.WithSimpleSchedule(x => x.WithIntervalInSeconds(2).RepeatForever())//每两秒执行一次
.Build();
       //4、创建任务
var jobDetail = JobBuilder.Create<MyJob>()
.WithIdentity("job", "group")
.Build();
       //5、将触发器和任务器绑定到调度器中
await _scheduler.ScheduleJob(jobDetail, trigger);
return await Task.FromResult(new string[] { "value1", "value2" });
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyJob : IJob//创建IJob的实现类,并实现Excute方法。
{
public Task Execute(IJobExecutionContext context)
{
      return Task.Run(() =>
  {
  using (StreamWriter sw = new StreamWriter(@"C:\Users\Administrator\Desktop\error.log", true, Encoding.UTF8))
  {
  sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss"));
  }
});
}
}
1
输出的结果: 2018-08-03 00-03-19 2018-08-03 00-03-20 2018-08-03 00-03-22 2018-08-03 00-03-24 2018-08-03 00-03-26

上面这种执行的Job没有参数,当需要参数可以通过下面两种方法传递参数:

1、在Trigger中添加参数值

1
2
3
4
5
6
var trigger3 = TriggerBuilder.Create()
.WithSimpleSchedule(x =>x.WithIntervalInSeconds(2).RepeatForever())//间隔2秒 一直执行
.UsingJobData("key1", 321) //通过在Trigger中添加参数值
.UsingJobData("key2", "123")
.WithIdentity("trigger2", "group1")
.Build();

2、在Job中添加参数值

1
2
3
4
5
IJobDetail job = JobBuilder.Create<MyJob>()
.UsingJobData("key1", 123)//通过Job添加参数值
.UsingJobData("key2", "123")
.WithIdentity("job1", "group1")
.Build();

通过下面方法在Job中获取参数值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
var jobData = context.JobDetail.JobDataMap;//获取Job中的参数

var triggerData = context.Trigger.JobDataMap;//获取Trigger中的参数

var data = context.MergedJobDataMap;//获取Job和Trigger中合并的参数

var value1= jobData.GetInt("key1");
var value2= jobData.GetString("key2");

var dateString = DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss");return Task.Run(() =>
{
using (StreamWriter sw = new StreamWriter(@"C:\Users\Administrator\Desktop\error.log", true, Encoding.UTF8))
{
sw.WriteLine(dateString);
}
});
}
}

当Job中的参数和Trigger中的参数名称一样时,用 context.MergedJobDataMap获取参数时,Trigger中的值会覆盖Job中的值。

3、上面那种情况只能适应那种,参数值不变的情况。假如有这种情况,这次的参数值是上一次执行后计算的值,就不能使用上面方法了。如 每两秒实现累加一操作,现在初始值是0,如果按照上面那种获取值的操作,一直都是0+1,返回值一直都是1。为了满足这个情况,只需要加一个特性[PersistJobDataAfterExecution]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//更新JobDetail的JobDataMap的存储副本,以便下一次执行这个任务接收更新的值而不是原始存储的值
[PersistJobDataAfterExecution]
public class MyJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
var jobData = context.JobDetail.JobDataMap;
var triggerData = context.Trigger.JobDataMap;
var data = context.MergedJobDataMap;

var value1 = jobData.GetInt("key1");
var value2 = jobData.GetString("key2");
var value3 = data.GetString("key2");

var dateString = DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss");
Random random = new Random();

jobData["key1"] = random.Next(1, 20);//这里面给key赋值,下次再进来执行的时候,获取的值为更新的值,而不是原始值
jobData["key2"] = dateString;

return Task.Run(() =>
{
using (StreamWriter sw = new StreamWriter(@"C:\Users\Administrator\Desktop\error.log", true, Encoding.UTF8))
{
sw.WriteLine($"{dateString} value1:{value1} value2:{value2}");
}
//context.Scheduler.DeleteJob(context.JobDetail.Key);
//context.Scheduler.Shutdown();
});
}
}

三、Quartz.Net组成

Quartz主要有三部分组成任务(Job)、触发器(Trigger)和调度器(Schedule)。

3.1 任务

Job就是执行的作业,Job需要继承IJob接口,实现Execute方法。Job中执行的参数从Execute方法的参数中获取。

3.2 触发器

触发器常用的有两种:SimpleTrigger触发器和CronTrigger触发器。

SimpleTrigger:能是实现简单业务,如每隔几分钟,几小时触发执行,并限制执行次数。

1
2
3
4
5
var trigger = TriggerBuilder.Create()
.WithSimpleSchedule(x => x.WithIntervalInSeconds(2).WithRepeatCount(5))//间隔2秒 执行6次
.UsingJobData("key1", 321)
.WithIdentity("trigger", "group")
.Build();

CronTrigger:Cron表达式包含7个字段,秒 分 时 月内日期 月 周内日期 年(可选)。

举例:

1
2
3
4
5
6
var trigger = TriggerBuilder.Create()
.WithCronSchedule("0 0 0 1 1 ?")// 每年元旦1月1日 0 点触发
.UsingJobData("key1", 321)
.UsingJobData("key2", "trigger-key2")
.WithIdentity("trigger4", "group14")
.Build();

“0 15 10 * * ? *” 每天上午10:15触发

“0 0-5 14 * * ?” 每天下午2点到下午2:05期间的每1分钟触发

3.3 调度器

调度器就是将任务和触发器绑定,让触发器触发的时候去执行任务。

https://www.cnblogs.com/MicroHeart/p/9402731.html

https://www.cnblogs.com/Leo_wl/p/8524600.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
using XXX.Infrastructure.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Dynamic.Core;

namespace XXX.Infrastructure.Extensions
{
public static class QueryableExtensions
{
public static IQueryable<T> ApplySort<T>(this IQueryable<T> source, string orderBy, IPropertyMapping propertyMapping)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}

if (propertyMapping == null)
{
throw new ArgumentNullException(nameof(propertyMapping));
}

var mappingDictionary = propertyMapping.MappingDictionary;
if (mappingDictionary == null)
{
throw new ArgumentNullException(nameof(mappingDictionary));
}

if (string.IsNullOrWhiteSpace(orderBy))
{
return source;
}

var orderByAfterSplit = orderBy.Split(',');
foreach (var orderByClause in orderByAfterSplit.Reverse())
{
var trimmedOrderByClause = orderByClause.Trim();
var orderDescending = trimmedOrderByClause.EndsWith(" desc");
var indexOfFirstSpace = trimmedOrderByClause.IndexOf(" ", StringComparison.Ordinal);
var propertyName = indexOfFirstSpace == -1 ?
trimmedOrderByClause : trimmedOrderByClause.Remove(indexOfFirstSpace);
if (string.IsNullOrEmpty(propertyName))
{
continue;
}
if (!mappingDictionary.TryGetValue(propertyName, out List<MappedProperty> mappedProperties))
{
throw new ArgumentException($"Key mapping for {propertyName} is missing");
}
if (mappedProperties == null)
{
throw new ArgumentNullException(propertyName);
}
mappedProperties.Reverse();
foreach (var destinationProperty in mappedProperties)
{
if (destinationProperty.Revert)
{
orderDescending = !orderDescending;
}
source = source.OrderBy(destinationProperty.Name + (orderDescending ? " descending" : " ascending"));
}
}

return source;
}

public static IQueryable<object> ToDynamicQueryable<TSource>
(this IQueryable<TSource> source, string fields, Dictionary<string, List<MappedProperty>> mappingDictionary)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}

if (mappingDictionary == null)
{
throw new ArgumentNullException(nameof(mappingDictionary));
}

if (string.IsNullOrWhiteSpace(fields))
{
return (IQueryable<object>)source;
}

fields = fields.ToLower();
var fieldsAfterSplit = fields.Split(',').ToList();
if (!fieldsAfterSplit.Contains("id", StringComparer.InvariantCultureIgnoreCase))
{
fieldsAfterSplit.Add("id");
}
var selectClause = "new (";

foreach (var field in fieldsAfterSplit)
{
var propertyName = field.Trim();
if (string.IsNullOrEmpty(propertyName))
{
continue;
}

var key = mappingDictionary.Keys.SingleOrDefault(k => String.CompareOrdinal(k.ToLower(), propertyName.ToLower()) == 0);
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException($"Key mapping for {propertyName} is missing");
}
var mappedProperties = mappingDictionary[key];
if (mappedProperties == null)
{
throw new ArgumentNullException(key);
}
foreach (var destinationProperty in mappedProperties)
{
selectClause += $" {destinationProperty.Name},";
}
}

selectClause = selectClause.Substring(0, selectClause.Length - 1) + ")";
return (IQueryable<object>)source.Select(selectClause);
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
 using System;
using System.Collections.Generic;
using System.Text;

namespace XXX.Infrastructure.Services
{
public interface IPropertyMapping
{
Dictionary<string, List<MappedProperty>> MappingDictionary { get; }
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 using XXX.Core.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;

namespace XXX.Infrastructure.Services
{
public abstract class PropertyMapping<TSource, TDestination> : IPropertyMapping
where TDestination : IEntity
{
public Dictionary<string, List<MappedProperty>> MappingDictionary { get; }

protected PropertyMapping(Dictionary<string, List<MappedProperty>> mappingDictionary)
{
MappingDictionary = mappingDictionary;
MappingDictionary[nameof(IEntity.Id)] = new List<MappedProperty>
{
new MappedProperty { Name = nameof(IEntity.Id), Revert = false}
};
}
}
}

1
query = query.ApplySort(postParameters.OrderBy, _propertyMappingContainer.Resolve<PostResource, Post>());
1
2
3
4
5
6
 public interface IPropertyMappingContainer
{
void Register<T>() where T : IPropertyMapping, new();
IPropertyMapping Resolve<TSource, TDestination>() where TDestination : IEntity;
bool ValidateMappingExistsFor<TSource, TDestination>(string fields) where TDestination : IEntity;
}

Startup.cs

1
2
3
4
var propertyMappingContainer = new PropertyMappingContainer();
propertyMappingContainer.Register<PostPropertyMapping>();
propertyMappingContainer.Register<ArticlePropertyMapping>();
services.AddSingleton<IPropertyMappingContainer>(propertyMappingContainer);