Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用
一,原理
首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的,也是Linux系统开发所用到的分布式版本控制软件。一说到分布式,其实就是每个电脑都有这么个版本库,而摒弃了传统的中心文件系统服务,所以每个人都能保存、还原之前的版本。在航天二院实习的时候,mentor让我改一个软件的Bug,由于国企没有外网,所以不能用github,院里面也没有搭建自己的Git文件服务器,所以每次改Bug之前都要备份一个之前的版本,以日期命名,而且后面还容易忘记那个版本有哪些功能模块,就我们两个人开发一个软件都把版本搞错了好几次,真心头痛。
二,Git常用命令
git的常用命令有:init、add、rm、mv、commit、push、pull、clone、log、checkout、megre、status、branch、diff、config、remote、fetch、reset、tag、show、stash、grep、rebase、gc等。
1,本地操作
Git命令 | 解释 | 撒旦法备注 |
git init | 初始化Git环境 | 先cd到要管理的文件夹中 |
git add *.txt / . | 添加文件到暂存区 | . 是将所有文件都添加到暂存区 |
git status | 查看文件的状态 | |
git commit -m 'First commit' | 提交更新到版本库 | -m 是message的缩写,即每次添加更新的备注 |
git log | 查看提交更新的记录 | |
git rm *.txt | 删除文件 | |
git mv a.txt b.txt | 修改文件名 | 将a.txt的文件名改为b.txt |
2,远程操作
Git命令 | 解释 | 撒旦法备注 |
git config --global user.name "your name" | 设置username,因为github每次commit都会记录他们 | github注册时的用户名 |
git config --global user.email "your_email@youremail.com" | 设置email | github注册时的邮箱地址 |
git remote add origin https://...git | 查看文件的状态与Github建立远程连接 | 现在github网站上建立仓库 |
git push -u origin master | 提交推送到Github的仓库中 | master分支可以是其他分支 |
在远程操作Github之前要进行ssh key的配置,网上有很多教程可以参考。大概分三步:
-
在本地创建ssh key,$ ssh-keygen -t rsa -C "your_email@youremail.com" (github网站上注册的邮箱),回车后会在当前用户.ssh文件夹下生成id_rsa、id_rsa.pub、known_hosts三个文件
-
打开id_rsa.pub,复制里面的key, 回到github网站,进入Account Settings,左边选择SSH Keys,Add SSH Key, Title随便填,粘贴key。
-
验证是否成功,在本地git bash下输入,$ ssh -T git@github.com ,回车就会看到:You’ve successfully authenticated, but GitHub does not provide shell access 。这就表示已成功连上github。
参考链接:
git中sshkey有何作用?
http://www.bootcss.com/p/git-guide/
Git命令脑图(图片来自Github)
3,Git分支管理模型Pic
三,Demo
1,初始化一个git仓库,新建一个code.txt文件,提交到git版本库,现在git开始管理code.txt文件了
ckjbug@ckjbug MINGW64 ~/Desktop $ mkdir gituse ckjbug@ckjbug MINGW64 ~/Desktop $ cd gituse ckjbug@ckjbug MINGW64 ~/Desktop/gituse $ git init Initialized empty Git repository in C:/Users/Enz/Desktop/gituse/.git/ ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) code.txt nothing added to commit but untracked files present (use "git add" to track) ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git add code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git commit -m 'First commit' [master (root-commit) 0f67f2c] First commit 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git status On branch master nothing to commit, working tree clean
2,修改文件,在code.txt添加一行代码,然后用checkout回到修改前(即没有添加代码前的版本,这里之前写入的代码没有了,空文本),这只是checkout的一种用法,更多是后面学习的git的分支转换。
ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: code.txt no changes added to commit (use "git add" and/or "git commit -a") ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git checkout -- code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ notepad code.txt
3,还是上面这个功能,回到文本修改前的状态,但是我们修改文件后已经添加到暂存区了,这时使用git checkout code.txt已经没用了,可以用git reset回到没有添加到暂存区的状态(最初始的状态)
ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ clear ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: code.txt no changes added to commit (use "git add" and/or "git commit -a") ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git add code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git checkout code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git reset Unstaged changes after reset: M code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: code.txt no changes added to commit (use "git add" and/or "git commit -a") ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git checkout -- code.txt
4,添加、切换、删除分支(添加develop开发者的工作分支)
ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git branch * master ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git branch develop ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git branch develop * master ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git checkout develop Switched to branch 'develop' M code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git branch * develop master ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git checkout master Switched to branch 'master' M code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git branch develop * master ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git branch -d develop Deleted branch develop (was 0f67f2c). ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git branch * master
5,新建一个开发者develop分支,然后添加develop.txt文本,添加到暂存库,提交,最后转换到master分支,发现develop.txt文本不见了,这就是版本管理,每个分支控制一个版本,当我们转回到develop分支下时这个devel.txt文件又出现了。如果我们在develop分支下修改了master分支的code.txt,当我们转换为master分支上时,不会出现develop分支下的修改。
ckjbug@ckjbug MINGW64 ~/Desktop/gituse $ git init Initialized empty Git repository in C:/Users/Enz/Desktop/gituse/.git/ ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) code.txt nothing added to commit but untracked files present (use "git add" to track) ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git add . ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git commit -m 'First commit' [master (root-commit) 2571887] First commit 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git status On branch master nothing to commit, working tree clean ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git branch * master ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git branch develop ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git checkout develop //之后添加一个develop.txt文件 Switched to branch 'develop' ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git branch * develop master ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git status On branch develop Untracked files: (use "git add <file>..." to include in what will be committed) develop.txt nothing added to commit but untracked files present (use "git add" to track) ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git add develop.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git commit -m 'add a develop_txt on branch develop' [develop 2fd0d60] add a develop_txt on branch develop 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 develop.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git branch * develop master ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git checkout master Switched to branch 'master' ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git checkout develop //在develop分支下修改master分支下的code.txt文件 Switched to branch 'develop' ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git status On branch develop Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: code.txt no changes added to commit (use "git add" and/or "git commit -a") ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git add code.txt ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git commit -m '在develop分支下修改了master分支的code.txt文件' [develop 903c8e0] 在develop分支下修改了master分支的code.txt文件 1 file changed, 1 insertion(+) ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git status On branch develop nothing to commit, working tree clean ckjbug@ckjbug MINGW64 ~/Desktop/gituse (develop) $ git checkout master //此时code.txt修改的代码不在了 Switched to branch 'master'
6,融合分支,在master分支上时,将develop分支merge到master分支上。这时develop.txt文件出现了。即融合到了master分支中。
ckjbug@ckjbug MINGW64 ~/Desktop/gituse (master) $ git merge develop Updating 2571887..903c8e0 Fast-forward code.txt | 1 + develop.txt | 0 2 files changed, 1 insertion(+) create mode 100644 develop.txt
7,Merge时冲突的处理,加入两个不同分支同时修改了主分支下code.txt中的第三行代码,两个分支在融合时会冲突。
8,远程推送push、拉取pull项目代码,在实际项目中,往往已经存在了远程的项目,当你参与到项目的开发当中时,
https://blog.csdn.net/weijinqian0/article/details/76217014
相关链接:
__EOF__
作 者:ckjbug
出 处:https://www.cnblogs.com/ckjbug/p/9761302.html
关于博主:编程路上的小学生,热爱技术,喜欢专研。评论和私信会在第一时间回复。或者直接私信我。
版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本。
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
Asp.net生命周期与Http协议
Http协议,底层的东西还是不是特别熟悉,感觉要经过沉淀之后才能理解这些东西吧
1.Asp.net生命周期
Asp.net生命周期:
从发起请求开始,到IIS进行处理的全部过程,然后再到获取结果
当请求一个*.aspx文件的时候,这个请求会被inetinfo.exe进程截获,它判断文件的后缀(aspx)之后,将这个请求转交给ASPNET_ISAPI.dll,ASPNET_ISAPI.dll会通过http管道(Http PipeLine)将请求发送给ASPNET_WP.exe进程,在ASPNET_WP.exe进程中通过HttpRuntime来处理这个请求,处理完毕将结果返回客户端。 inetinfo.exe进程:是www服务的进程,IIS服务和ASPNET_ISAPI.DLL都寄存在此进程中。 ASPNET_ISAPI.DLL:是处理.aspx文件的win32组件。其实IIS服务器是只能识别.html文件的,当IIS服务器发现被请求的文件是.aspx文件时,IIS服务器将其交给aspnet_isapi.dll来处理。 aspnet_wp.exe进程:ASP.NET框架进程,提供.net运行的托管环境,.net的CLR(公共语言运行时)就是寄存在此进程中。
ASP.NET Framework处理一个Http Request的流程: HttpRequest-->inetinfo.exe-->ASPNET_ISAPI.dll-->ASPNET_WP.exe-->HttpRuntime-->HttpApplication Factory-->HttpApplication-->HttpModule-->HttpHandler Factory-->HttpHandler-->HttpHandler.ProcessRequest()
ASP.NET请求处理过程是基于管道模型的,这个管道模型是由多个HttpModule和HttpHandler组成,ASP.NET把http请求依次传递给管道中各个HttpModule,最终被HttpHandler处理,处理完成后,再次经过管道中的HTTP模块,把结果返回给客户端。我们可以在每个HttpModule中都可以干预请求的处理过程。
2.Htpp协议的生命周期
这个感觉要后续继续加深理解之后再进行补充吧,目前就拿别人的思路来解答这个过程
1.输入url,浏览器DNS解析域名,获取ip
2.三次握手,建立tcp链接
3.向服务端发送http请求
4.服务端处理请求并响应
5.浏览器渲染HTML
6在渲染的过程中继续加载css,js,图片,音频,视频文件
7 呈现给用户
托管代码与非托管代码的区别
其实是知道这个东西,但是不知道专业术语居然叫这个,我以为是Azure云托管,好吧,开个玩笑
不止C#,java也是托管代码啊,重点,重点,重点!!!
1.简单的说,就是代码被编译成MSIL后在.net的Framework下运行,同操作系统底层的交互都交给framework去做。所谓非托管代码就是脱离了Framework的管制,
直接同底层API打交道,自己管理自己的内存和安全机制等东西。而托管代码就不管这些,全都由Framework去完成
2.“程序”一般都是在对操作系统进行直接或者间接的操作
“托管程序”是需要通过访问公共语言运行时(cls)才能访问操作系统的程序
而“非托管程序”不用通过访问公共语言运行时(cls)可以直接访问操作系统的程序
3.vb.net,C#等写的程序是托管程序,VC++可以写托管程序,如果用到了内存管理,则只能编译为非托管程序这些东西MSDN都有描述
通过IEnumerable接口遍历数据
使用IEnumerable接口遍历数据,这在项目中会经常的用到,这个类型呢主要是一个枚举器。
1.首先需要让该类型实现一个名字叫IEnumerable的接口,实现该接口的主要目的是为了让当前类型中增加一个名字叫GetEnumerator()的方法。
public class Person : IEnumerable { private string[] Friends = new string[] { "张三", "李四", "王五", "赵六" }; public string Name { get; set; } public int Age { get; set; } public string Email { get; set; } #region IEnumerable 成员 //这个方法的作用就是返回一个“枚举器” public IEnumerator GetEnumerator() { return new PersonEnumerator(this.Friends); } #endregion }
2.希望一个类型被枚举遍历,就是要实现一个枚举器方法
public class PersonEnumerator : IEnumerator { public PersonEnumerator(string[] fs) { _friends = fs; } private string[] _friends; //一般下标都是一开始指向了第一条的前一条。第一条是0 private int index = -1; #region IEnumerator 成员 public object Current { get { if (index >= 0 && index < _friends.Length) //下标有范围 { return _friends[index]; } else { throw new IndexOutOfRangeException(); } } } public bool MoveNext() { if (index + 1 < _friends.Length) //下标有范围 { index++; return true; } return false; } public void Reset() { index = -1; } #endregion }
3.然后进行遍历,这里呢可以调用自己封装的MoveNext方法去找数组元素
Person p = new Person(); IEnumerator etor = p.GetEnumerator(); while (etor.MoveNext()) { Console.WriteLine(etor.Current.ToString()); }
也可以直接使用foreach,而且主要是因为是枚举元素,类似与数组,list等等之类的,都可以使用Lambda表达式来进行数据的处理
Person p = new Person(); foreach (string item in p) { Console.WriteLine(item); } Console.WriteLine("ok");
4.输出的结果如下:
依赖注入与控制反转
反正这个概念我一般都是不去记得,首先看一下什么是依赖:
有一个类是Animal,然后我定义了一个BlackCat类,类里面有一个BlackCat方法,那么这里的BlackCat就依赖Animal
public class BlackCat { public BlackCat(Animal Cat) { Cry(); } }
BlackCat类实例化的时候需要一个Animal的对象作为构造函数的参数,那么BlackCat就依赖Animal,这就叫依赖。
当然,不用构造函数的方式,在BlackCat类内部去new一个Animal,也是依赖;当然注入的话,就像是你写了一个类,然后
通过IOC框架,把这个类注入到其他类中,这就是注入
控制反转的意思就好理解了,就比如我定义了一个类,类里面有一个方法,然后我现在要把这个方法的控制权交给别人来使用,这就是控制反转。
在编写代码的时候,我们需要把一些接口编写成通用的道理就在这里了,便于做到代码复用
下面即以猫的例子来进行解说控制反转
1.先定义一个动物类
using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace IOC { class Animal { public void Cry() { Console.Write("动物喊叫"); } } }
2.定义一个猫的类
using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace IOC { class Cat:Animal { public void Cry() { Console.WriteLine("动物喊叫"); } } }
3.我用实例化一个动物类,然后查看结果
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace IOC { class Program { static void Main(string[] args) { Animal A = new Cat(); A.Cry(); Console.ReadLine(); } } }
4.可以看到我用子类可以替换掉父类,也可以用父类替换掉子类,其实并没有太大的影响
Animal A = new Cat();
可以看见输出结果如下
C#多线程——优先级
在我的公司这里,因为要跟很多特殊的设备打交道,所以会用到多线程的东西,那么我们在进行多线程处理的时候,怎么去设置优先级
我这里用听歌和下载小说做了个例子,我们用电脑的时候肯定是可以边听歌边下载小说的,那么这就需要并行,有个问题就是我想优先听
歌,下载小说对我来说不是那么急的话我就可以对两个事情进行优先级的管控。
线程里有个属性Priority可以用来设置优先级,我设置线程1的优先级高于线程2的优先级,那么线程1就会比线程2多运行一段时间,这个是人眼观察不出来的
运行速度,CPU运行速度可不是能用人眼查看的
bool b = true; int i=0, j=0; string Song = ""; string Download = ""; Thread Thread1=new Thread(() => { while (b) { Song="一百万个可能"; i++; } }) { Name = "Thread1", Priority = ThreadPriority.Highest }; Thread Thread2=new Thread(() => { while (b) { Download = "小说三体"; j++; } }) { Name = "Thread2", Priority = ThreadPriority.Lowest }; Thread1.Start(); Thread2.Start(); Thread.Sleep(1000); b = false; Console.WriteLine("Song: {0}, Download: {1}", Song, Download); Console.WriteLine("歌曲的优先级:{0}",i); Console.WriteLine("下载的优先级:{0}",j); Console.ReadLine();
这里我们看一下执行结果
从结果中可以看到,优先级高的线程得到运行的次数比优先级低的线程多,但即使是最低优先级的线程都有很大的机会来执行。
AutoFac容器初步
转载请注明出处,AutoFac:最流行的依赖注入和IOC框架,轻量且高性能,对项目代码几乎无任何侵入性。
那么我们怎么来使用这样一个框架呢
1、在引用项右击,选择Nuget管理,这里我们要导入两个包
一个是AutoFac包,另外一个就是Autofac ASP.NET MVC5 Intergration
在webapi里面使用的话我们需要添加一个Autofac ASP.NET Web API2.2 Intergration 才可以。
2.Global.asax.cs属性注入,配置IOC容器,我这里配置的是通用的以I开头的Repository(仓库类)
#region autofac IOC容器配置 var builder = new ContainerBuilder(); //注册所有的controller builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired(); //注册所有模块module builder.RegisterAssemblyModules(Assembly.GetExecutingAssembly()); var assemblys = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray(); //注册所有继承IDependency接口的类 builder.RegisterAssemblyTypes(assemblys) .Where(type => typeof(IDependency).IsAssignableFrom(type) && !type.IsAbstract); //注册服务,所有IxxxxRepository=>xxxxRepository builder.RegisterAssemblyTypes(assemblys).Where(t => t.Name.EndsWith("Repository") && !t.Name.StartsWith("I")).AsImplementedInterfaces(); var container = builder.Build(); BaseInfo._container = container; DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); #endregion
3.新建实体基类以及实体类,也就是创建数据模型
base类:
public class BaseEntity { [NotMapped] [PropertyModelBinder("start")] public int pageIndex { get; set; } [NotMapped] [PropertyModelBinder("length")] public int pageSize { get; set; } [NotMapped] public string draw { get; set; } [NotMapped] public List<Orderby> order { get; set; } [NotMapped] public List<Datacolumn> columns { get; set; } } public class Orderby { public string column { get; set; } public string dir { get; set; } } public class Datacolumn { public string data { get; set; } public string name { get; set; } public bool searchable { get; set; } public bool orderable { get; set; } }
实体类:
public class User : BaseEntity { public User() { RoleList = new List<Role>(); MessageList = new List<UserMappingMessage>(); } public int UserID { get; set; } public string UserName { get; set; } public string UserPassword {get;set;} public string UserReallyname {get;set;} public string HeadPortrait { get; set; } public string MobilePhone { get; set; } public string Email { get; set; } public int DepartmentID {get;set;} public bool IsEnable {get;set;} public DateTime CreateTime {get;set;} public DateTime? UpdateTime {get;set;} public string Remark { get; set; } public ICollection<Role> RoleList { get; set; } //接收消息列表 public ICollection<UserMappingMessage> MessageList { get; set; } public Dictionary Department { get; set; } //发送消息列表 public List<Message> Messages { get; set; } }
4.创建仓储接口,因为我在配置Global属性时,已经说明了我要注入所有以 “I” 开头的接口,那么我就把这个用户的接口给定义为IUserRepository
当然,不同的实体类所需要的仓储接口也不一样,这里根据自己的实际需求去写需要的方法类,接口嘛,就不要在这里具体实现你的方法了
public interface IUserRepository { Tuple<int,List<User>> GetList(User model); List<User> GetUserInfos(); User GetSingle(User model); User GetbyID(int userID); void AddUser(User model); void ModifyUser(User model); void DeleteUser(User model); void SetUserInfoRole(int userID, List<int> roleIDList); List<AutoUserDo> GetUserInfobyName(string value); void ResetUserPWDbyID(int id); }
5.我们定义一个实现接口的类,然后把接口的方法给实现,这里面就是对数据实体进行操作了
public class UserRepository : IUserRepository { public Tuple<int, List<User>> GetList(User model) { using (UnitOfWork dal=BaseInfo._container.Resolve<UnitOfWork>()) { var SysUserRepository = dal.GetRepository<User>(); var conditions = ExpandHelper.True<User>(); if (!string.IsNullOrEmpty(model.UserName)) conditions = conditions.And(a => a.UserName.Contains(model.UserName)); if (!string.IsNullOrEmpty(model.UserReallyname)) conditions = conditions.And(a => a.UserReallyname.Contains(model.UserReallyname)); if (model.DepartmentID > 0) conditions = conditions.And(a => a.DepartmentID == model.DepartmentID); var templist = SysUserRepository.Get(filter: conditions, includeProperties: "RoleList"); var count = templist.Count(); if (model.order != null&&model.order.Count()>0) { foreach (var item in model.order) { var column = model.columns.ElementAt(int.Parse(item.column)); templist = templist.OrderSort(column.data, item.dir); } } var result = templist.PageBy(model.pageIndex, model.pageSize).ToList(); return new Tuple<int, List<User>>(count, result); } } public User GetSingle(User model) { using(UnitOfWork dal=BaseInfo._container.Resolve<UnitOfWork>()){ var conditions = ExpandHelper.True<User>(); if (!string.IsNullOrEmpty(model.UserName)) conditions = conditions.And( a => a.UserName == model.UserName || a.MobilePhone == model.UserName); if (!string.IsNullOrEmpty(model.MobilePhone)) conditions = conditions.And(a => a.MobilePhone == model.MobilePhone); var result = dal.GetRepository<User>().Get(conditions).FirstOrDefault(); return result; } } public User GetbyID(int userID) { using (UnitOfWork dal = BaseInfo._container.Resolve<UnitOfWork>()) { // var result = dal.GetRepository<User>().Get(filter: a => a.UserID == userID, includeProperties: "RoleList.MenuList,RoleList.rbList").AsNoTracking().FirstOrDefault(); var result = dal.GetRepository<User>().Get(filter: a => a.UserID == userID,includeProperties: "RoleList").FirstOrDefault(); foreach (var item in result.RoleList) { var role=dal.GetRepository<Role>().Get(a=>a.RoleID==item.RoleID,includeProperties:"MenuList,rbList").FirstOrDefault(); item.MenuList = role.MenuList; item.rbList = role.rbList; } return result; } } public void AddUser(User model) { using (UnitOfWork dal = BaseInfo._container.Resolve<UnitOfWork>()) { dal.GetRepository<User>().Insert(model); dal.Save(); } } public void ModifyUser(User model) { using (UnitOfWork dal = BaseInfo._container.Resolve<UnitOfWork>()) { dal.GetRepository<User>().UpdateSup(model, new List<string>() { "IsEnable", "CreateTime" }, false); dal.Save(); } } public void DeleteUser(User model) { using (UnitOfWork dal = BaseInfo._container.Resolve<UnitOfWork>()) { var sysUserRepository= dal.GetRepository<User>(); var Usermodel = sysUserRepository.GetByID(model.UserID); Usermodel.IsEnable=Usermodel.IsEnable?false:true; sysUserRepository.UpdateSup(Usermodel, new List<string>() { "IsEnable" }); dal.Save(); } } /// <summary> /// 添加用户角色信息,先删除原有数据,在添加到数据库 /// </summary> /// <param name="userID"></param> /// <param name="roleIDList"></param> /// <returns></returns> public void SetUserInfoRole(int userID, List<int> roleIDList) { using (UnitOfWork dal = BaseInfo._container.Resolve<UnitOfWork>()) { var sysUserRepository = dal.GetRepository<User>(); var roleRepository = dal.GetRepository<Role>(); var UserModel = GetbyID(userID); var roleList = UserModel.RoleList.ToList(); roleList.ForEach(m => { var userModel = sysUserRepository.Get(filter: a => a.UserID == userID, includeProperties: "RoleList").FirstOrDefault(); var roleModel = roleRepository.GetByID(m.RoleID); userModel.RoleList.Remove(roleModel); }); roleIDList.ForEach(m => { var userModel = sysUserRepository.GetByID(userID); var roleModel = roleRepository.GetByID(m); userModel.RoleList.Add(roleModel); }); dal.Save(); } } public List<AutoUserDo> GetUserInfobyName(string value) { Mapper.Initialize(a => { a.CreateMap<User, AutoUserDo>() .ForMember(au => au.id, op => { op.MapFrom(user => user.UserID); }) .ForMember(au => au.text, op => { op.MapFrom(user => user.UserReallyname); }) .ForMember(au => au.department, op => { op.MapFrom(user => user.Department.DicValue); }); a.CreateMap<Role, roleinfo>(); }); using (var dal = BaseInfo._container.Resolve<UnitOfWork>()) { return dal.GetRepository<User>() .Get(a => a.UserReallyname.Contains(value) || a.MobilePhone == value, includeProperties: "Department,Role").ProjectToQueryable<AutoUserDo>().ToList(); } } public void ResetUserPWDbyID(int id) { using (var dal = BaseInfo._container.Resolve<UnitOfWork>()) { var repository = dal.GetRepository<User>(); var usermodel = new User() { UserID = id, UserPassword = "123456" }; repository.UpdateSup(usermodel, new List<string>() { "UserPassword" }); dal.Save(); } } public List<User> GetUserInfos() { using (var dal = BaseInfo._container.Resolve<UnitOfWork>()) { return dal.GetRepository<User>().Get().ToList(); } } }
6.然后我们就可以在MVC的控制器里面去调用这些方法来实现我们所想要的功能了,我这里只展示一个方法
public void SendEmailAsync(Message model) { Task.Run(() => { try { var reclist = string.Empty; foreach (var item in model.RecUser.Split(',')) { var userinfo = this.UserRepository.GetbyID(int.Parse(item)); //这里就调用了实现接口的那个类的方法,去验证用户的ID if (!string.IsNullOrEmpty(userinfo.Email)) { reclist += userinfo.Email + ","; } } if (!string.IsNullOrEmpty(reclist)) { reclist = reclist.Substring(0, reclist.Length - 1); EmailHelper email = new EmailHelper(reclist, model.MessageTitle, model.MessageText); email.Send(); } model.SendEmailState = 2; this.MessageServer.SetSendState(model); }catch(Exception ex){ new LogHelper().LogError("发送邮件异常" + ex); model.SendEmailState = 3; this.MessageServer.SetSendState(model); } }); }
那么整个流程就是这样,看着别人写的那些博客里面的流程不是那么的全面,我这里就详细的把AutoFac的整个流程给梳理出来了,有不对的地方请及时指出
后面我会详细说明一下Unity IOC框架是如何使用的,这里我就不再叙述了
下面是两篇比较好的博文,我觉得比较有参考意义的,可以看一下,喜欢我发布的内容的可以关注我,后面还会有其他的干货和内容进行分享
.NET领域最为流行的IOC框架之一Autofac:https://www.cnblogs.com/yinrq/p/5381492.html
.NET Unity IOC框架使用实例:https://blog.csdn.net/chen_peng7/article/details/54896449
C#特性详解
特性(attribute)是被指定给某一声明的一则附加的声明性信息。
在C#中,有一个小的预定义特性集合。在学习如何建立我们自己的定制特性(custom attributes)之前,我们先来看看在我们的代码中如何使用预定义特性。
1 using System; 2 public class AnyClass 3 { 4 [Obsolete("Don't use Old method, use New method", true)] 5 static void Old( ) { } 6 static void New( ) { } 7 public static void Main( ) 8 { 9 Old( ); 10 } 11 }
我们先来看一下上面这个例子,在这个例子中我们使用了Obsolete特性,它标记了一个不应该再被使用的程序实体。第一个参数是一个字符串,它解释了为什么该实体是过时的以及应该用什么实体来代替它。实际上,你可以在这里写任何文本。第二个参数告诉编译器应该把使用这个过时的程序实体当作一种错误。它的默认值是false,也就是说编译器对此会产生一个警告。
当我们尝试编译上面这段程序的时候,我们将会得到一个错误:
AnyClass.Old()' is obsolete: 'Don't use Old method, use New method'
开发定制特性(custom attributes)
现在让我们来看看如何开发我们自己的特性。
首先我们要从System.Attribute派生出我们自己的特性类(一个从System.Attribute抽象类继承而来的类,不管是直接还是间接继承,都会成为一个特性类。特性类的声明定义了一种可以被放置在声明之上新的特性)。
1 using System; 2 public class HelpAttribute : Attribute 3 { 4 }
不管你是否相信,我们已经建立了一个定制特性,现在我们可以用它来装饰现有的类就好像上面我们使用Obsolete attribute一样。
1 [Help()] 2 public class AnyClass 3 { 4 }
注意:对一个特性类名使用Attribute后缀是一个惯例。然而,当我们把特性添加到一个程序实体,是否包括 Attribute后缀是我们的自由。编译器会首先在System.Attribute的派生类中查找被添加的特性类。如果没有找到,那么编译器会添加 Attribute后缀继续查找。
到目前为止,这个特性还没有起到什么作用。下面我们来添加些东西给它使它更有用些。
1 using System; 2 public class HelpAttribute : Attribute 3 { 4 public HelpAttribute(String Descrition_in) 5 { 6 this.description = Description_in; 7 } 8 protected String description; 9 public String Description 10 { 11 get 12 { 13 return this.description; 14 } 15 } 16 } 17 [Help("this is a do-nothing class")] 18 public class AnyClass 19 { 20 }
在上面的例子中,我们给HelpAttribute特性类添加了一个属性并且在后续的部分中我们会在运行时环境中查寻它。
定义或控制特性的使用
AttributeUsage类是另外一个预定义特性类,它帮助我们控制我们自己的定制特性的使用。它描述了一个定制特性如和被使用。
AttributeUsage有三个属性,我们可以把它放置在定制属性前面。第一个属性是:
ValidOn
通过这个属性,我们能够定义定制特性应该在何种程序实体前放置。一个属性可以被放置的所有程序实体在AttributeTargets enumerator中列出。通过OR操作我们可以把若干个AttributeTargets值组合起来。
AllowMultiple
这个属性标记了我们的定制特性能否被重复放置在同一个程序实体前多次。
Inherited
我们可以使用这个属性来控制定制特性的继承规则。它标记了我们的特性能否被继承。
下面让我们来做一些实际的东西。我们将会在刚才的Help特性前放置AttributeUsage特性以期待在它的帮助下控制Help特性的使用。
1 using System; 2 [AttributeUsage(AttributeTargets.Class), AllowMultiple = false, 3 Inherited = false ] 4 public class HelpAttribute : Attribute 5 { 6 public HelpAttribute(String Description_in) 7 { 8 this.description = Description_in; 9 } 10 protected String description; 11 public String Description 12 { 13 get 14 { 15 return this.description; 16 } 17 } 18 }
先让我们来看一下AttributeTargets.Class。它规定了Help特性只能被放在class的前面。这也就意味着下面的代码将会产生错误:
1 [Help("this is a do-nothing class")] 2 public class AnyClass 3 { 4 [Help("this is a do-nothing method")] //error 5 public void AnyMethod() 6 { 7 } 8 }
编译器报告错误如下:
AnyClass.cs: Attribute 'Help' is not valid on this declaration type.
It is valid on 'class' declarations only.
我们可以使用AttributeTargets.All来允许Help特性被放置在任何程序实体前。可能的值是:
Assembly,Module,Class,Struct,Enum,Constructor,Method,Property,Field,Event,Interface,Parameter,Delegate
All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate
ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface
下面考虑一下AllowMultiple = false。它规定了特性不能被重复放置多次。
1 [Help("this is a do-nothing class")] 2 [Help("it contains a do-nothing method")] 3 public class AnyClass 4 { 5 [Help("this is a do-nothing method")] //error 6 public void AnyMethod() 7 { 8 } 9 }
它产生了一个编译期错误。
AnyClass.cs: Duplicate 'Help' attribute
Ok,现在我们来讨论一下最后的这个属性。Inherited, 表明当特性被放置在一个基类上时,它能否被派生类所继承。
1 [Help("BaseClass")] 2 public class Base 3 { 4 } 5 public class Derive : Base 6 { 7 }
这里会有四种可能的组合:
1 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false ] 2 [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false ] 3 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true ] 4 [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true ]
第一种情况:
如果我们查询(Query)(稍后我们会看到如何在运行期查询一个类的特性)Derive类,我们将会发现Help特性并不存在,因为inherited属性被设置为false。
第二种情况:
和第一种情况相同,因为inherited也被设置为false。
第三种情况:
为了解释第三种和第四种情况,我们先来给派生类添加点代码:
1 [Help("BaseClass")] 2 public class Base 3 { 4 } 5 [Help("DeriveClass")] 6 public class Derive : Base 7 { 8 }
现在我们来查询一下Help特性,我们只能得到派生类的属性,因为inherited被设置为true,但是AllowMultiple却被设置为false。因此基类的Help特性被派生类Help特性覆盖了。
第四种情况:
在这里,我们将会发现派生类既有基类的Help特性,也有自己的Help特性,因为AllowMultiple被设置为true。
定义或控制特性的使用AttributeUsage类是另外一个预定义特性类,它帮助我们控制我们自己的定制特性的使用。它描述了一个定制特性如何被使用。
属性和特性的区别可以参考一下: http://developer.51cto.com/art/200908/147097.htm
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Foxalien/archive/2009/12/05/4946672.aspx
C#特性详解
特性(attribute)是被指定给某一声明的一则附加的声明性信息。
在C#中,有一个小的预定义特性集合。在学习如何建立我们自己的定制特性(custom attributes)之前,我们先来看看在我们的代码中如何使用预定义特性。
1 using System; 2 public class AnyClass 3 { 4 [Obsolete("Don't use Old method, use New method", true)] 5 static void Old( ) { } 6 static void New( ) { } 7 public static void Main( ) 8 { 9 Old( ); 10 } 11 }
我们先来看一下上面这个例子,在这个例子中我们使用了Obsolete特性,它标记了一个不应该再被使用的程序实体。第一个参数是一个字符串,它解释了为什么该实体是过时的以及应该用什么实体来代替它。实际上,你可以在这里写任何文本。第二个参数告诉编译器应该把使用这个过时的程序实体当作一种错误。它的默认值是false,也就是说编译器对此会产生一个警告。
当我们尝试编译上面这段程序的时候,我们将会得到一个错误:
AnyClass.Old()' is obsolete: 'Don't use Old method, use New method'
开发定制特性(custom attributes)
现在让我们来看看如何开发我们自己的特性。
首先我们要从System.Attribute派生出我们自己的特性类(一个从System.Attribute抽象类继承而来的类,不管是直接还是间接继承,都会成为一个特性类。特性类的声明定义了一种可以被放置在声明之上新的特性)。
1 using System; 2 public class HelpAttribute : Attribute 3 { 4 }
不管你是否相信,我们已经建立了一个定制特性,现在我们可以用它来装饰现有的类就好像上面我们使用Obsolete attribute一样。
1 [Help()] 2 public class AnyClass 3 { 4 }
注意:对一个特性类名使用Attribute后缀是一个惯例。然而,当我们把特性添加到一个程序实体,是否包括 Attribute后缀是我们的自由。编译器会首先在System.Attribute的派生类中查找被添加的特性类。如果没有找到,那么编译器会添加 Attribute后缀继续查找。
到目前为止,这个特性还没有起到什么作用。下面我们来添加些东西给它使它更有用些。
1 using System; 2 public class HelpAttribute : Attribute 3 { 4 public HelpAttribute(String Descrition_in) 5 { 6 this.description = Description_in; 7 } 8 protected String description; 9 public String Description 10 { 11 get 12 { 13 return this.description; 14 } 15 } 16 } 17 [Help("this is a do-nothing class")] 18 public class AnyClass 19 { 20 }
在上面的例子中,我们给HelpAttribute特性类添加了一个属性并且在后续的部分中我们会在运行时环境中查寻它。
定义或控制特性的使用
AttributeUsage类是另外一个预定义特性类,它帮助我们控制我们自己的定制特性的使用。它描述了一个定制特性如和被使用。
AttributeUsage有三个属性,我们可以把它放置在定制属性前面。第一个属性是:
ValidOn
通过这个属性,我们能够定义定制特性应该在何种程序实体前放置。一个属性可以被放置的所有程序实体在AttributeTargets enumerator中列出。通过OR操作我们可以把若干个AttributeTargets值组合起来。
AllowMultiple
这个属性标记了我们的定制特性能否被重复放置在同一个程序实体前多次。
Inherited
我们可以使用这个属性来控制定制特性的继承规则。它标记了我们的特性能否被继承。
下面让我们来做一些实际的东西。我们将会在刚才的Help特性前放置AttributeUsage特性以期待在它的帮助下控制Help特性的使用。
1 using System; 2 [AttributeUsage(AttributeTargets.Class), AllowMultiple = false, 3 Inherited = false ] 4 public class HelpAttribute : Attribute 5 { 6 public HelpAttribute(String Description_in) 7 { 8 this.description = Description_in; 9 } 10 protected String description; 11 public String Description 12 { 13 get 14 { 15 return this.description; 16 } 17 } 18 }
先让我们来看一下AttributeTargets.Class。它规定了Help特性只能被放在class的前面。这也就意味着下面的代码将会产生错误:
1 [Help("this is a do-nothing class")] 2 public class AnyClass 3 { 4 [Help("this is a do-nothing method")] //error 5 public void AnyMethod() 6 { 7 } 8 }
编译器报告错误如下:
AnyClass.cs: Attribute 'Help' is not valid on this declaration type.
It is valid on 'class' declarations only.
我们可以使用AttributeTargets.All来允许Help特性被放置在任何程序实体前。可能的值是:
Assembly,Module,Class,Struct,Enum,Constructor,Method,Property,Field,Event,Interface,Parameter,Delegate
All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate
ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface
下面考虑一下AllowMultiple = false。它规定了特性不能被重复放置多次。
1 [Help("this is a do-nothing class")] 2 [Help("it contains a do-nothing method")] 3 public class AnyClass 4 { 5 [Help("this is a do-nothing method")] //error 6 public void AnyMethod() 7 { 8 } 9 }
它产生了一个编译期错误。
AnyClass.cs: Duplicate 'Help' attribute
Ok,现在我们来讨论一下最后的这个属性。Inherited, 表明当特性被放置在一个基类上时,它能否被派生类所继承。
1 [Help("BaseClass")] 2 public class Base 3 { 4 } 5 public class Derive : Base 6 { 7 }
这里会有四种可能的组合:
1 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false ] 2 [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false ] 3 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true ] 4 [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true ]
第一种情况:
如果我们查询(Query)(稍后我们会看到如何在运行期查询一个类的特性)Derive类,我们将会发现Help特性并不存在,因为inherited属性被设置为false。
第二种情况:
和第一种情况相同,因为inherited也被设置为false。
第三种情况:
为了解释第三种和第四种情况,我们先来给派生类添加点代码:
1 [Help("BaseClass")] 2 public class Base 3 { 4 } 5 [Help("DeriveClass")] 6 public class Derive : Base 7 { 8 }
现在我们来查询一下Help特性,我们只能得到派生类的属性,因为inherited被设置为true,但是AllowMultiple却被设置为false。因此基类的Help特性被派生类Help特性覆盖了。
第四种情况:
在这里,我们将会发现派生类既有基类的Help特性,也有自己的Help特性,因为AllowMultiple被设置为true。
定义或控制特性的使用AttributeUsage类是另外一个预定义特性类,它帮助我们控制我们自己的定制特性的使用。它描述了一个定制特性如何被使用。
属性和特性的区别可以参考一下: http://developer.51cto.com/art/200908/147097.htm
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Foxalien/archive/2009/12/05/4946672.aspx
WPF 可触摸移动的ScrollViewer控件
ListBox支持触摸滑动,而ScrollViewer默认不支持。
ScrollViewer如需要添加上下/左右触摸移动,需要在Touch事件中处理。
处理如下:封装成一个用户控件
- TouchDown事件中记录起始点,并添加对TouchMove事件的监听
- TouchUp事件中注销TouchMove事件的监听
- 在TouchMove事件中,处理移动的偏移量。起始位置减去偏移量,即为当前滚动条的位置。
注:ScrollViewer滚动到指定位置(指定位置=起始位置-移动的偏移量,滚动方向和手势方向相反)
1 /// <summary> 2 /// 可触摸滚动的ScrollViewer控件 3 /// </summary> 4 public class TouchableScrollViewer : ScrollViewer 5 { 6 //触摸点的坐标 7 Point _startPosition; 8 //滚动条当前位置 9 double _startVerticalOffset; 10 double _startHorizontalOffset; 11 public TouchableScrollViewer() 12 { 13 TouchDown += TouchableScrollViewer_TouchDown; 14 15 TouchUp += TouchableScrollViewer_TouchUp; 16 } 17 private void TouchableScrollViewer_TouchDown(object sender, TouchEventArgs e) 18 { 19 //添加触摸移动监听 20 TouchMove -= TouchableScrollViewer_TouchMove; 21 TouchMove += TouchableScrollViewer_TouchMove; 22 23 //获取ScrollViewer滚动条当前位置 24 _startVerticalOffset = VerticalOffset; 25 _startHorizontalOffset = HorizontalOffset; 26 27 //获取相对于ScrollViewer的触摸点位置 28 TouchPoint point = e.GetTouchPoint(this); 29 _startPosition = point.Position; 30 } 31 32 private void TouchableScrollViewer_TouchUp(object sender, TouchEventArgs e) 33 { 34 //注销触摸移动监听 35 TouchMove -= TouchableScrollViewer_TouchMove; 36 } 37 38 private void TouchableScrollViewer_TouchMove(object sender, TouchEventArgs e) 39 { 40 //获取相对于ScrollViewer的触摸点位置 41 TouchPoint endPoint = e.GetTouchPoint(this); 42 //计算相对位置 43 double diffOffsetY = endPoint.Position.Y - _startPosition.Y; 44 double diffOffsetX = endPoint.Position.X - _startPosition.X; 45 46 //ScrollViewer滚动到指定位置(指定位置=起始位置-移动的偏移量,滚动方向和手势方向相反) 47 ScrollToVerticalOffset(_startVerticalOffset - diffOffsetY); 48 ScrollToHorizontalOffset(_startHorizontalOffset - diffOffsetX); 49 } 50 }
Demo下载
.NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用
.NET程序员一定最熟悉所见即所得式开发,熟悉的Visual Studio开发界面,熟悉的C#代码。
Smobiler也是因为具备这样的特性,使开发人员,可以在VisualStudio上,像开发WinForm一样拖拉控件,让许多人在开发APP时,再次回到所见即所得的开发方式中去。
Smobiler的快速开发,让Amanda看到了程序员们分享的各式各样的应用。
一
来自程序员的分享
👆产线物料管理类的应用
实时监控产线物料情况
社区物业管理类的应用👇
方便社区居民在线查询、缴费
👆公益社区类的应用
为公益热心者提供一个线上社区
企业内部OA管理应用👇
请假、工作流、报销等功能
以上截图均来自Smobiler技术开发群用户的分享
二
一千个应用有一千种界面
在“家庭小秘”没出现之前,我们也是想不到会有用户愿意在UI上投入资源,做出出这样简洁好看的界面。
家庭小秘APP
毕竟我们见到更多的是企业类应用,对UI界面要求没那么高,功能至上。
比如这个花了十来天做的企业内部管理应用:
一款为练手而做的APP
这只是练手之作,咱们先把后端的业务代码跑通,UI设计什么的,正式项目里,交给专业的设计师会更省事。
专业的和业余虽然都能把意思表达出来,但区别就跟下面这张图一样:
三
官方推出的三款开源APP
Smobiler官方也先后推出了三款开源的应用,应用的源代码已经托管至GitHub,这三款应用分别是
SmoONE:开源的移动OA应用
包含了GPS定位、IM等功能
SmoSEC:开源的资产管理移动应用
包含了条码扫描、RFID扫描等功能
SmoWMS:开源的仓库管理移动应用
包含仓库管理中的基本核心功能,当然仓库管理中的条码扫描、RFID扫描等也是不可少的
(文末有这三款应用源代码的获取方式。)
重点是,这三款应用的代码,都是
免费!开源的!
你可以把源码下载下来,然后根据项目的需求,进行二次开发。比如这样
左图是SmoONE的界面,右图是用户在SmoONE源码基础上的修改
比如这样:
左图是SmoWMS的界面,右图是用户在SmoWMS源码基础上的修改
这样的界面从零做起,总共只分三步,大概需要两分钟吧:
第一步:拖拉控件
第二步:设置title和toolbar属性
第三步:设置iconmenuview属性和启动程序
最后做什么样的APP,完全看项目需要,和程序员的心情,以及……客户是否介意你依照自己的喜好做出来的界面。
做项目嘛,最重要的是效率;敲代码嘛,最重要的就是开心。
四
三款开源项目的获取方式
在GitHub上搜索
“SmoONE”、“SmoSEC”、“SmoWMS”
便可找到。
也可复制以下地址至浏览器直接前往获取。
SmoONE 移动OA应用
https://github.com/comsmobiler/SmoONE
包含了GPS定位、IM等功能
SmoSEC 资产管理移动应用
https://github.com/comsmobiler/SmoSEC
资产管理移动应用,包含了条码扫描、RFID扫描等功能
SmoWMS 仓库管理移动应用
https://github.com/comsmobiler/SmoWMS
包含仓库管理中的基本核心功能,当然仓库管理中的条码扫描、RFID扫描等也是不可少的
.NET移动开发,关于发布IOS的方法(本人亲身经历折腾很久终于成功)
前情提要:这位.NET程序员兄弟使用Smobiler开发了一个APP,尽管Smobiler云平台已经最大限度的简化了iOS应用的打包操作,但仍绕不开苹果公司强制要求的p12文件,p12文件需要开发者自行生成,在此,qio763分享了此次生成p12文件的经验,无论是初学iOS原生开发,还是.NET移动开发平台的smobiler,在生成iOS安装包之前,p12文件生成这一步都是必经之路。
(P.S.提交了正确的p12文件后,应用已成功打包)
----------------------------------以下为原文----------------------------------
在发布IOS版本前,需要做的准备工作:
本人使用的虚拟主机,版本为10.12(但不支持xcode10有点尴尬),如果你使用的MAC系统,可以直接操作,虚拟主机方面请自行百度,此处不讲
第一步,生成一个你的个人证书(钥匙串)
第二步:进入IOS开发者中心进行相关的设置(此处非常重要,很多人包括我本人都出现了错误)
点击Certificates下的all弹出的菜单中点击+号,新添加一个你的个人证书,如已有证书可跳过此步
需要注意此步聚很重要,此处必须选择红框部分,因为smo发布要求发布正式版本,所以需要选择此项,然后点击continue直到出现以下画面
点击红框部分,选择刚才我们使用钥匙串申请的文件
完成后点击download下载到本地,双击刚下载的证书,将其导入到钥匙串中
添加完成后,我们需要将证书生成P12个人证书,这也是smo所需要的证书,我们在钥匙串中请行以下操作
右键点击我们刚才添加的证书,选择导出证书
导出证书时,文件格式默认为P12,我们就不要动了,也不要去管他,默认就好。
导出证书时需填写一个你的导出密码,这个密码可以随意设置,但必须牢记,对应smobier中的导出密码
到此,证书部分就算是完成了,企业证书原理一样,操作方法也是这样。下面是创建我们的APPID与我们的发布描述。
发布描述部分相对比较麻烦,很多用户出错基本都在这里出错(我自己在这出错好几次)
发布描述,首先需要创建APPID
Identifiers》appids中点击+号
需要注意的是BundleID必须与你的smobier的应用包名一致,如果不一致将无法正常打包
必须勾选Push Notifications选项,包含了推送信息,然后点击继续按钮直到完成
点击刚创建的appid弹出详细信息,我们会发现,该功能并没有应用,我们点击edit进行编辑
我们会发现,关于Push Notifications部分有两个选项,其实一个是测试版,一个是正式版,我们这里选择正式版并创建,点击继续按钮进入选择页面
点击选择按钮,我们选择,我们最开始用钥匙串生成的文件,点击继续完成appid Push Notifications的修改,至此,appid创建完成,接下来就是发布描述文件的生成了
Provisioning Profiles→Distribution
点击Distribution中的+号
在此处我们选择正式版,也就是红色框部分,点击继续
此处选择,我们刚才创建的appid,此ID对应的是我们的smobiler的包名
选择我们第一步创建的证书,点击继续,完成发布描述,点击download下载我们的发布描述文件
我们在smobiler的应用平台发布IOS时,就将我们刚才生成的P12文件上传,密码填写我们导出P12证书的密码,将下载的发布描述文件上传后,就可以完成IOS的打包了
打包IOS很重要,特别是你的插件,如果包名错误了,再删除是很难恢复的,我的插件就是因为打包操作出现问题就没有了,很难过
作者:qio763
(原帖地址:https://www.smobiler.com/forum.php?mod=viewthread&tid=11605)