基础设施层主要包含3个项目:
一、数据实体,对应到数据的表和视图
在这里有个基础实体类,用来限定repository的泛型实体传入,也重载了一些方法hashcode之类的,方便做数据实体比较,还有一个辅助生成主键的类,代码如下:
1. Entity类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chart.Workflow.Infrastructure.Data
{
public class Entity
{
#region Members
int? _requestedHashCode;
string _Id;
#endregion
#region Properties
/// <summary>
/// Get the persisten object identifier
/// </summary>
public virtual string Id
{
get
{
return _Id;
}
protected set
{
_Id = value;
}
}
#endregion
#region Public Methods
/// <summary>
/// Check if this entity is transient, ie, without identity at this moment
/// </summary>
/// <returns>True if entity is transient, else false</returns>
public bool IsTransient()
{
return string.IsNullOrEmpty(this.Id);
}
/// <summary>
/// Generate identity for this entity
/// </summary>
public void GenerateNewIdentity()
{
if (IsTransient())
this.Id = IdentityGenerator.NewSequentialId();
}
/// <summary>
/// Change current identity for a new non transient identity
/// </summary>
/// <param name="identity">the new identity</param>
public void ChangeCurrentIdentity(string identity)
{
if (!string.IsNullOrEmpty(this.Id))
this.Id = identity;
}
#endregion
#region Overrides Methods
/// <summary>
/// <see cref="M:System.Object.Equals"/>
/// </summary>
/// <param name="obj"><see cref="M:System.Object.Equals"/></param>
/// <returns><see cref="M:System.Object.Equals"/></returns>
public override bool Equals(object obj)
{
if (obj == null || !(obj is Entity))
return false;
if (Object.ReferenceEquals(this, obj))
return true;
Entity item = (Entity)obj;
if (item.IsTransient() || this.IsTransient())
return false;
else
return item.Id == this.Id;
}
/// <summary>
/// <see cref="M:System.Object.GetHashCode"/>
/// </summary>
/// <returns><see cref="M:System.Object.GetHashCode"/></returns>
public override int GetHashCode()
{
if (!IsTransient())
{
if (!_requestedHashCode.HasValue)
_requestedHashCode = this.Id.GetHashCode() ^ 31;
// XOR for random distribution
(http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx)
return _requestedHashCode.Value;
}
else
return base.GetHashCode();
}
public static bool operator ==(Entity left, Entity right)
{
if (Object.Equals(left, null))
return (Object.Equals(right, null)) ? true : false;
else
return left.Equals(right);
}
public static bool operator !=(Entity left, Entity right)
{
return !(left == right);
}
#endregion
}
}
2. IdentityGenerator类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chart.Workflow.Infrastructure.Data
{
public static class IdentityGenerator
{
/// <summary>
/// This algorithm generates secuential strings across system boundaries, ideal for databases
/// </summary>
/// <returns></returns>
public static string NewSequentialId()
{
string resut = Guid.NewGuid().ToString().Replace("-", "");
return resut;
}
/// <summary>
/// This algorithm generates secuential strings across system boundaries, ideal for databases
/// </summary>
/// <returns></returns>
public static Guid NewSequentialGuid()
{
byte[] uid = Guid.NewGuid().ToByteArray();
byte[] binDate = BitConverter.GetBytes(DateTime.UtcNow.Ticks);
byte[] secuentialGuid = new byte[uid.Length];
secuentialGuid[0] = uid[0];
secuentialGuid[1] = uid[1];
secuentialGuid[2] = uid[2];
secuentialGuid[3] = uid[3];
secuentialGuid[4] = uid[4];
secuentialGuid[5] = uid[5];
secuentialGuid[6] = uid[6];
// set the first part of the 8th byte to '1100' so
// later we'll be able to validate it was generated by us
secuentialGuid[7] = (byte)(0xc0 | (0xf & uid[7]));
// the last 8 bytes are sequential,
// it minimizes index fragmentation
// to a degree as long as there are not a large
// number of Secuential-Guids generated per millisecond
secuentialGuid[9] = binDate[0];
secuentialGuid[8] = binDate[1];
secuentialGuid[15] = binDate[2];
secuentialGuid[14] = binDate[3];
secuentialGuid[13] = binDate[4];
secuentialGuid[12] = binDate[5];
secuentialGuid[11] = binDate[6];
secuentialGuid[10] = binDate[7];
return new Guid(secuentialGuid);
}
}
}
二、辅助切面,主要用来全局的东西:包含验证,日志,资源之类的;
基础方法如下:
三、数据上下文,主要是基础的数据访问操作实现
1. ISql接口,做sql语句查询使用,如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chart.Workflow.Infrastructure.Context
{
public interface ISql
{
IEnumerable<TEntity> ExecuteQuery<TEntity>(string sqlQuery, params object[] parameters);
int ExecuteCommand(string sqlCommand, params object[] parameters);
}
}
2. IQueryUnitOfWork类,简单数据的操作,如下:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chart.Workflow.Infrastructure.Context
{
public interface IQueryUnitOfWork:IUnitOfWork,ISql
{
DbSet<TEntity> CreateSet<TEntity>() where TEntity : class;
void Attach<TEntity>(TEntity item) where TEntity : class;
void SetModified<TEntity>(TEntity item) where TEntity : class;
void ApplyCurrentValues<TEntity>(TEntity original, TEntity current) where TEntity : class;
}
}
3. IUnitOfWork接口,数据事务性操作,如下:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chart.Workflow.Infrastructure.Context
{
public interface IUnitOfWork : IDisposable
{
void Commit();
void CommitAndRefreshChanges();
void RollbackChanges();
}
}
4. UnitOfWork类,具体核心实现数据操作,如下:
using Chart.Workflow.Infrastructure.Data;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chart.Workflow.Infrastructure.Context
{
public class UnitOfWork : DbContext, IQueryUnitOfWork
{
IDbSet<Student> _students;
public IDbSet<Student> Students
{
get
{
if (_students == null)
_students = base.Set<Student>();
return _students;
}
}
IDbSet<Course> _course;
public IDbSet<Course> Courses
{
get
{
if (_course == null)
_course = base.Set<Course>();
return _course;
}
}
public UnitOfWork()
:base()
{
}
public UnitOfWork(string connectionstring)
:base(connectionstring)
{
}
#region IQueryUnitOfWork Members
public DbSet<TEntity> CreateSet<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
public void Attach<TEntity>(TEntity item) where TEntity : class
{
//attach and set as unchanged
base.Entry<TEntity>(item).State = System.Data.EntityState.Unchanged;
}
public void SetModified<TEntity>(TEntity item) where TEntity : class
{
//this operation also attach item in object state manager
base.Entry<TEntity>(item).State = System.Data.EntityState.Modified;
}
public void ApplyCurrentValues<TEntity>(TEntity original, TEntity current) where TEntity : class
{
//if it is not attached, attach original and set current values
base.Entry<TEntity>(original).CurrentValues.SetValues(current);
}
public void Commit()
{
base.SaveChanges();
}
public void CommitAndRefreshChanges()
{
bool saveFailed = false;
do
{
try
{
base.SaveChanges();
saveFailed = false;
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
ex.Entries.ToList()
.ForEach(entry =>
{
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
});
}
} while (saveFailed);
}
public void RollbackChanges()
{
// set all entities in change tracker
// as 'unchanged state'
base.ChangeTracker.Entries()
.ToList()
.ForEach(entry => entry.State = System.Data.EntityState.Unchanged);
}
public IEnumerable<TEntity> ExecuteQuery<TEntity>(string sqlQuery, params object[] parameters)
{
if (parameters == null)
{
parameters = new object[0];
}
return base.Database.SqlQuery<TEntity>(sqlQuery, parameters);
}
public int ExecuteCommand(string sqlCommand, params object[] parameters)
{
if (parameters == null)
{
parameters = new object[0];
}
return base.Database.ExecuteSqlCommand(sqlCommand, parameters);
}
#endregion
#region DbContext Overrides
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Remove unused conventions
//OneToManyCascadeDeleteConvention
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
//数据库表和实体类型不一致使用
modelBuilder.Entity<Student>().ToTable("Student");
modelBuilder.Entity<Course>().ToTable("Course");
//数据库字段条件限制配置文件
modelBuilder.Configurations.Add(new StudentEntityTypeConfiguration());
modelBuilder.Configurations.Add(new CourseEntityTypeConfiguration());
}
#endregion
}
}