Asp.net MVC Api项目搭建
整个解决方案按照分层思想来划分不同功能模块,以提供User服务的Api为需求,各个层次的具体实现如下所示:
1、新建数据库User表
数据库使用SQLExpress版本,表的定义如下所示:
CREATE TABLE [dbo].[User] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
[Password] NVARCHAR (50) NOT NULL,
[Age] INT NOT NULL,
[Birthdate] DATE NOT NULL,
[CreateTime] DATETIME DEFAULT (getdate()) NOT NULL,
[CreateUserId] NVARCHAR (50) NOT NULL,
[CreateUserName] NVARCHAR (50) NOT NULL,
[ModifiedTime] DATETIME NULL,
[ModifiedUserId] NVARCHAR (50) NULL,
[ModifiedUserName] NVARCHAR (50) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
2、实体层
新建类库类型的项目,.net framework框架版本4.5,User实体类如下:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public int Age { get; set; }
public DateTime Birthdate { get; set; }
public DateTime CreateTime { get; set; }
public string CreateUserId { get; set; }
public string CreateUserName { get; set; }
public DateTime? ModifiedTime { get; set; }
public string ModifiedUserId { get; set; }
public string ModifiedUserName { get; set; }
}
3、数据库层
该层提供数据库接口操作,采用EntitFramework4.4.0.0作为ORM实体映射框架,
- 定义数据库操作接口IRepository
public interface IRepository<TEntity> : IDisposable where TEntity : class
{
IEnumerable<TEntity> Get();
IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter);
IEnumerable<TEntity> Get<TOderKey>(Expression<Func<TEntity, bool>> filter, int pageIndex, int pageSize, Expression<Func<TEntity, TOderKey>> sortKeySelector, bool isAsc = true);
int Count(Expression<Func<TEntity, bool>> predicate);
void Update(TEntity instance);
void Add(TEntity instance);
void Delete(TEntity instance);
}
- 定义数据库上下文类BceDbContext
public class BceDbContext:DbContext
{
public BceDbContext():base("DaLeiDB")
{
Database.SetInitializer<AceDbContext>(null);
}
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("User");
base.OnModelCreating(modelBuilder);
}
}
- 定义数据库通用操作实现类BceRepository
public class BceRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
public BceDbContext DbContext { get; private set; }
public DbSet<TEntity> DbSet { get; private set; }
public BceRepository(BceDbContext context)
{
Guard.ArgumentNotNull(context, "context");
this.DbContext = context;
this.DbSet = this.DbContext.Set<TEntity>();
}
public IEnumerable<TEntity> Get()
{
return this.DbSet.AsQueryable();
}
public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter)
{
return this.DbSet.Where(filter).AsQueryable();
}
public IEnumerable<TEntity> Get<TKey>(Expression<Func<TEntity, bool>> filter, int pageIndex, int pageSize, Expression<Func<TEntity, TKey>> sortKeySelector, bool isAsc = true)
{
Guard.ArgumentNotNull(filter, "predicate");
Guard.ArgumentNotNull(sortKeySelector, "sortKeySelector");
if (isAsc)
{
return this.DbSet
.Where(filter)
.OrderBy(sortKeySelector)
.Skip(pageSize * (pageIndex - 1))
.Take(pageSize).AsQueryable();
}
else
{
return this.DbSet
.Where(filter)
.OrderByDescending(sortKeySelector)
.Skip(pageSize * (pageIndex - 1))
.Take(pageSize).AsQueryable();
}
}
public int Count(Expression<Func<TEntity, bool>> predicate)
{
return this.DbSet.Where(predicate).Count();
}
public void Add(TEntity instance)
{
Guard.ArgumentNotNull(instance, "instance");
this.DbSet.Attach(instance);
this.DbContext.Entry(instance).State = EntityState.Added;
this.DbContext.SaveChanges();
}
public void Update(TEntity instance)
{
Guard.ArgumentNotNull(instance, "instance");
this.DbSet.Attach(instance);
this.DbContext.Entry(instance).State = EntityState.Modified;
this.DbContext.SaveChanges();
}
public void Delete(TEntity instance)
{
Guard.ArgumentNotNull(instance, "instance");
this.DbSet.Attach(instance);
this.DbContext.Entry(instance).State = EntityState.Deleted;
this.DbContext.SaveChanges();
}
public void Dispose()
{
this.DbContext.Dispose();
}
}
- 定义用户表数据库操作额外的接口IUserRepository
namespace DaLei.Repository
{
public interface IUserRepository
{
User FindByName(string name);
List<User> FindByAge(int start, int end);
}
}
- 定义用户表数据库操作额外的实现类UserRepository
namespace DaLei.Repository { public class UserRepository : BceRepository<User>, IUserRepository { public UserRepository(BceDbContext bceDbContext):base(bceDbContext) { } public List<User> FindByAge(int start, int end) { var rst = this.DbSet.AsQueryable().Where(u=>u.Age>=start && u.Age<=end).ToList(); return rst; } public User FindByName(string name) { var rst = this.DbSet.AsQueryable().Where(u => u.Name == name).FirstOrDefault(); return rst; } } }
4、服务接口层
定义了对外提供用户服务的接口契约,具体如下:
namespace DaLei.IService
{
public interface IUserService
{
User FindByName(string name);
List<User> FindByAge(int start, int end);
List<User> GetList();
}
}
5、服务具体实现层
服务基类实现:
using System;
using System.Collections.Generic;
namespace DaLei.Service
{
public abstract class ServiceBase : IDisposable
{
public IList<IDisposable> DisposableObjects { get; private set; }
public ServiceBase()
{
this.DisposableObjects = new List<IDisposable>();
}
protected void AddDisposableObject(object obj)
{
IDisposable disposable = obj as IDisposable;
if (null != disposable)
{
this.DisposableObjects.Add(disposable);
}
}
public void Dispose()
{
foreach (IDisposable obj in this.DisposableObjects)
{
if (null != obj)
{
obj.Dispose();
}
}
}
}
}
用户服务接口具体实现:
using DaLei.IService;
using DaLei.Model;
using DaLei.Repository;
using System.Collections.Generic;
namespace DaLei.Service
{
public class UserService : ServiceBase,IUserService
{
private IUserRepository userRepository;
public UserService(IUserRepository userRepository)
{
this.userRepository = userRepository;
this.AddDisposableObject(userRepository);
}
public List<User> FindByAge(int start, int end)
{
return this.userRepository.FindByAge(start,end);
}
public User FindByName(string name)
{
return this.userRepository.FindByName(name);
}
public List<User> GetList()
{
return this.userRepository.FindByAge(0, 100);
}
}
}
6、应用层
新建Asp.net WebApi项目,引用服务接口项目、服务实现项目、实体类项目,unity相关程序集,EntityFramework程序集。
web.config配置连接字符串和unity相关内容:
<?xml version="1.0" encoding="utf-8"?>
<!--
有关如何配置 ASP.NET 应用程序的详细信息,请访问
https://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>
<unity>
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
<containers>
<container>
<extension type="Interception" />
<register type="DaLei.IService.IUserService, DaLei.IService" mapTo="DaLei.Service.UserService, DaLei.Service" />
<register type="DaLei.Repository.IUserRepository, DaLei.Repository" mapTo="DaLei.Repository.UserRepository, DaLei.Repository">
<interceptor type="InterfaceInterceptor" />
<policyInjection />
</register>
</container>
</containers>
</unity>
<connectionStrings>
<add name="DaLeiDB" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=DaleiDB;User ID=sa;Password=anpiel0991;" providerName="System.Data.SqlClient" />
</connectionStrings>
<appSettings>
<add key="webpages:Version" value="3.0.0.0"/>
<add key="webpages:Enabled" value="false"/>
<add key="PreserveLoginUrl" value="true"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5"/>
<httpRuntime targetFramework="4.5"/>
<pages>
<namespaces>
<add namespace="System.Web.Helpers"/>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
<add namespace="System.Web.Mvc.Html"/>
<add namespace="System.Web.Routing"/>
<add namespace="System.Web.WebPages"/>
</namespaces>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
<remove name="OPTIONSVerbHandler"/>
<remove name="TRACEVerbHandler"/>
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0"/>
</handlers>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed"/>
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-5.2.7.0" newVersion="5.2.7.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701"/>
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+"/>
</compilers>
</system.codedom>
</configuration>
- Controller实例化由Unity负责处理,需要实现控制器工厂类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using System.Configuration;
namespace WebApi.Extensions
{
public class UnityControllerFactory : DefaultControllerFactory
{
static object syncHelper = new object();
static Dictionary<string, IUnityContainer> containers = new Dictionary<string, IUnityContainer>();
public IUnityContainer UnityContainer { get; private set; }
public UnityControllerFactory(string containerName = "")
{
if (containers.ContainsKey(containerName))
{
this.UnityContainer = containers[containerName];
return;
}
lock (syncHelper)
{
if (containers.ContainsKey(containerName))
{
this.UnityContainer = containers[containerName];
return;
}
IUnityContainer container = new UnityContainer();
//配置UnityContainer
UnityConfigurationSection configSection = ConfigurationManager.GetSection(UnityConfigurationSection.SectionName) as UnityConfigurationSection;
if (null == configSection && !string.IsNullOrEmpty(containerName))
{
throw new ConfigurationErrorsException("The <unity> configuration section does not exist.");
}
if (null != configSection)
{
if (string.IsNullOrEmpty(containerName))
{
configSection.Configure(container);
}
else
{
configSection.Configure(container, containerName);
}
}
containers.Add(containerName, container);
this.UnityContainer = containers[containerName];
}
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (null == controllerType)
{
return null;
}
return (IController)this.UnityContainer.Resolve(controllerType);
}
//public override void ReleaseController(IController controller)
//{
// this.UnityContainer.Teardown(controller);
//}
}
}
- 设置MVC框架的控制器工厂为自定义类型
using webApi.Extensions;
using System;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
namespace WebApp
{
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// 在应用程序启动时运行的代码
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory());
}
}
}
- 新建Controller基类,所有的自定义Controller均继承此类
using WebApi.Extensions;
using System.Web.Mvc;
using System;
using System.Collections.Generic;
namespace WebApp.Controllers
{
public class BaseController:Controller
{
public IList<IDisposable> DisposableObjects { get; private set; }
public BaseController()
{
this.DisposableObjects = new List<IDisposable>();
}
protected void AddDisposableObject(object obj)
{
IDisposable disposable = obj as IDisposable;
if (null != disposable)
{
this.DisposableObjects.Add(disposable);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (IDisposable obj in this.DisposableObjects)
{
if (null != obj)
{
obj.Dispose();
}
}
}
base.Dispose(disposing);
}
}
}
- 新建测试Controller
using DaLei.IService;
using System.Web.Mvc;
namespace WebApp.Controllers
{
public class TestController : BaseController
{
private IUserService userService;
public TestController(IUserService userService)
{
this.userService = userService;
this.AddDisposableObject(userService);
}
// GET: Test
public ActionResult Index()
{
var users = this.userService.GetList();
return View();
}
}
}
浏览器输入地址测试TestController!