你的位置:首页 > ASP.net教程

[ASP.net教程]在ASP.NET Web API中使用OData的单例模式


 

从OData v4开始增加了对单例模式的支持,我们不用每次根据主键等来获取某个EDM,就像在C#中使用单例模式一样。实现方式大致需要两步:

 

1、在需要实现单例模式的导航属性上加上[Singleton]特性
2、在EDM配置的时候使用builder.Singleton<SomeModel>("SomeModels")来创建SingletonConfiguration<SomeModel>

 

首先还是从模型开始。

 

public class Employee{  public int ID { get; set; }  public string Name { get; set; }  [Singleton]  public Company Company { get; set; }}public enum CompanyCategory{  IT = 0,  Communication = 1,  Electronics = 2,  Others = 3}public class Company{  public int ID { get; set; }  public string Name { get; set; }  public Int64 Revenue { get; set; }  public CompanyCategory Category { get; set; }  public List<Employee> Employees { get; set; }}

 

以上,Company和Employee存在1对多关系,我们在Employee的Compnay导航属性上加上了[Singleton]特性,也就意味着我们希望在Company上使用单例模式。

 

然后就在WebApiConfig中配置如下:

 

public static class WebApiConfig{  public static void Register(HttpConfiguration config)  {    ...    config.MapODataServiceRoute("ODataRoute", "odata", GetEdmModel());  }  public static IEdmModel GetEdmModel()  {    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();    EntitySetConfiguration<Employee> employeesConfiguration = builder.EntitySet<Employee>("Employees");    EntityTypeConfiguration<Employee> employeeTypeConfiguration = employeesConfiguration.EntityType;    employeeTypeConfiguration.Action("ResetDataSource");    SingletonConfiguration<Company> companiesConfiguration = builder.Singleton<Company>("Umbrella");    companiesConfiguration.EntityType.Action("ResetDataSource");    companiesConfiguration.EntityType.Function("GetEmployeesCount").Returns<int>();    builder.Namespace = "Hello";    return builder.GetEdmModel();  }}

 

以上,builder.Singleton<Company>("Umbrella")方法创建SingletonConfiguration<Company>类型的实例,这是EDM实现单例的方式。

 


Company对应的控制器UmbrellaController

 

再来看Company对应的控制器,大致如下:

 

public class UmbrellaController : ODataController{  public static Company Umbrella;  static UmbrellaController()  {    InitData();  }  private static void InitData()  {    Umbrella = new Company()    {      ID = 1,      Name = "Umbrella",      Revenue = 1000,      Category = CompanyCategory.Communication,      Employees = new List<Employee>()    };  }    ...    [HttpPost]  public IHttpActionResult ResetDataSourceOnCompany()  {    InitData();    return StatusCode(HttpStatusCode.NoContent);  }  public IHttpActionResult GetEmployeesCount()  {    return Ok(Umbrella.Employees.Count);  }}

 

以上,UmbrellaController提供的静态Company类型的Umbrella可以在全局获取。ResetDataSourceOnCompany对应配置单例EDM的companiesConfiguration.EntityType.Action("ResetDataSource")的Action,GetEmployeesCount对应配置单例EDM的companiesConfiguration.EntityType.Function("GetEmployeesCount").Returns<int>()的Function。

 

● 查询

 

[EnableQuery]public IHttpActionResult Get(){  return Ok(Umbrella);}public IHttpActionResult GetRevenueFromCompany(){  return Ok(Umbrella.Revenue);}public IHttpActionResult GetName(){  return Ok(Umbrella.Employees);}

 

以上,GetRevenueFromCompany和GetName分别获取属性,要符合惯例,即"Get+属性名称"。

 

● 添加

 

public IHttpActionResult Put(Company newCompany){  Umbrella = newCompany;  return StatusCode(HttpStatusCode.NoContent);}

 

● Patch

 

public IHttpActionResult Patch(Delta<Company> item){  item.Patch(Umbrella);  return StatusCode(HttpStatusCode.NoContent);}

 

● 创建Company上的Employees关系

 

/// <summary>/// 创建Company上Employees的关系/// </summary>/// <param name="navigationProperty"></param>/// <param name="link">Empolyee的uri地址</param>/// <returns></returns>[AcceptVerbs("POST")]public IHttpActionResult CreateRef(string navigationProperty, [FromBody] Uri link){  //获取Employee的外键  int employeeId = HelperFunction.GetKeyValue<int>(link);  Employee employee = EmployeesController.Employees.First(x => x.ID == employeeId);  if(employee == null || navigationProperty!="Employees")  {    return BadRequest();  }  if(Umbrella.Employees == null)  {    Umbrella.Employees = new List<Employee>() { employee};  }  else  {    Umbrella.Employees.Add(employee);  }  return StatusCode(HttpStatusCode.NoContent);}

 

其实就是往Company的Employees集合导航属性中添加一个元素。其中,HelperFunction.GetKeyValue<int>()方法用来获取link中Empoyee的主键。如下:

 

public static class HelperFunction{  //获取主键值  public static TKey GetKeyValue<TKey>(Uri uri)  {    if(uri ==null)    {      throw new ArgumentException("uri");    }    var rootPath = uri.AbsoluteUri.Substring(0, uri.AbsoluteUri.LastIndexOf('/') + 1);    var odataUriParser = new ODataUriParser(WebApiConfig.GetEdmModel(), new Uri(rootPath), uri);    var odataPath = odataUriParser.ParsePath();    var keySegment = odataPath.LastSegment as KeySegment;    if(keySegment==null)    {      throw new InvalidOperationException("The link does not contain a key");    }    return (TKey)keySegment.Keys.First().Value;  }}

 

● 删除Company上的Employees关系

 

/// <summary>/// 删除关系/// </summary>/// <param name="relatedKey">Employee的主键</param>/// <param name="navigationProperty"></param>/// <returns></returns>public IHttpActionResult DeleteRef(string relatedKey, string navigationProperty){  int key = int.Parse(relatedKey);  Employee employee = Umbrella.Employees.First(x => x.ID == key);  if(navigationProperty != "Employees")  {    return BadRequest();  }  Umbrella.Employees.Remove(employee);  return StatusCode(HttpStatusCode.NoContent);}

 

其实就是删除Company的集合属性Employees中的一个Employee元素。

 


● 往Company的Employees集合里添加一个Employee元素

 

/// <summary>/// 从Compnay处添加某个Employee/// </summary>/// <param name="employee"></param>/// <returns></returns>[HttpPost]public IHttpActionResult PostToEmployees([FromBody] Employee employee){  EmployeesController.Employees.Add(employee);  if(Umbrella.Employees == null)  {    Umbrella.Employees = new List<Employee>() { employee };  }  else  {    Umbrella.Employees.Add(employee);  }  return Created(employee);}

 

EmployeesController不详诉

 

public class EmployeesController : ODataController{  public static List<Employee> Employees;  static EmployeesController()  {    InitData();  }  private static void InitData()  {    Employees = Enumerable.Range(0, 10).Select(i =>        new Employee()        {          ID = i,          Name = string.Format("Name {0}", i)        }).ToList();  }  [EnableQuery]  public IHttpActionResult Get()  {    return Ok(Employees.AsQueryable());  }  [EnableQuery]  public IHttpActionResult Get(int key)  {    return Ok(Employees.Where(e => e.ID == key));  }  public IHttpActionResult GetCompanyFromEmployee([FromODataUri] int key)  {    var company = Employees.First(e => e.ID == key).Company;    if(company==null)    {      return StatusCode(HttpStatusCode.NotFound);    }    return Ok(company);  }  public IHttpActionResult Post([FromBody] Employee employee)  {    Employees.Add(employee);    return Created(employee);  }  [AcceptVerbs("PUT")]  public IHttpActionResult CreateRef([FromODataUri] int key, string navigationProperty, [FromBody] Uri link)  {    if(navigationProperty!="Company")    {      return BadRequest();    }    Employees.First(e => e.ID == key).Company = UmbrellaController.Umbrella;    return StatusCode(HttpStatusCode.NoContent);  }  public IHttpActionResult DeleteRef([FromODataUri] int key, string navigationProperty)  {    if(navigationProperty!="Company")    {      return BadRequest();    }    Employees.First(e => e.ID == key).Company = null;    return StatusCode(HttpStatusCode.NoContent);  }  public IHttpActionResult PutToCompany(int key, Company company)  {    var navigateCompany = Employees.First(e => e.ID == key).Company;    Employees.First(e => e.ID == key).Company = company;    if(navigateCompany.Name == "Umbrella")    {      //体现Singleton      UmbrellaController.Umbrella = navigateCompany;    }    else    {      return BadRequest();    }    return StatusCode(HttpStatusCode.NoContent);  }  public IHttpActionResult PatchToCompany(int key, Delta<Company> company)  {    var navigateCompan = Employees.First(e => e.ID == key).Company;    company.Patch(Employees.First(e => e.ID == key).Company);    if(navigateCompan.Name == "Umbrella")    {      company.Patch(UmbrellaController.Umbrella);    }    else    {      return BadRequest();    }    return StatusCode(HttpStatusCode.NoContent);  }  [HttpPost]  public IHttpActionResult ResetDataSourceOnCollectionOfEmployee()  {    InitData();    return Ok();  }}