总之一句话,客户端代码能够只依赖接口或者抽象类或基类或其他,而不关心运行时由谁来提供具体实现。
使用IoC容器如SharePoint Service Locator,首先配置依赖关系(即当向Ioc容器询问特定的类型时将返回一个具体的实现),所以这又叫依赖注入。
MVP在项目中的实践
有了上面的分析,那么就来设计漂亮的代码:
模块化代码
松耦合,无依赖
代码重用
独立的单元测试
首先创建IVew,单纯的给UI界面"取"数据和"显示"数据
public interface IEmployeeView
{
string Country { get; }
IEnumerable<EmployeeModel> EmplyeeList { set; }
bool NotEmployeesFoundMessageVisible { set; }
}
接着WebPart实现IView
[ToolboxItemAttribute(false)]
public partial class VisualWebPart1 : WebPart,IEmployeeView
{
// Uncomment the following SecurityPermission attribute only when doing Performance Profiling on a farm solution
// using the Instrumentation method, and then remove the SecurityPermission attribute when the code is ready
// for production. Because the SecurityPermission attribute bypasses the security check for callers of
// your constructor, it's not recommended for production purposes.
// [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Assert, UnmanagedCode = true)]
private EmployeePresenter _presenter;
public VisualWebPart1()
{
IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent(SPContext.Current.Site);
IEmployeeRepository employeeRepository = serviceLocator.GetInstance<IEmployeeRepository>();
_presenter = new EmployeePresenter(this, employeeRepository);
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
InitializeControl();
}
protected void Page_Load(object sender, EventArgs e)
{
_presenter.GetEmployees();
}
public string Country
{
get { return HttpContext.Current.Request["country"] }
}
public IEnumerable<Model.EmployeeModel> EmplyeeList
{
set
{
rptDataSource.DataSource = value;
rptDataSource.DataBind();
}
}
public bool NotEmployeesFoundMessageVisible
{
set { lblMessage.Visible = value; }
}
}
接着对BaseRepository的设计
public abstract class BaseRepository<T>
{
protected SPWeb _web;
public BaseRepository()
{
}
public BaseRepository(SPWeb web)
{
_web = web;
}
protected IEnumerable<T> GetEntities(SPListItemCollection items)
{
List<T> list =null;
if (items.Count>0)
{
list = new List<T>();
foreach (SPListItem item in items)
{
list.Add(GetEntity(item));
}
}
return list;
}
protected abstract T GetEntity(SPListItem item);
}
正如前面分析的那样,基于接口的设计能更好的做单元测试,所以创建IRepository
public interface IEmployeeRepository
{
IEnumerable<EmployeeModel> GetEmployeeByCountry(string country);
}
实现Repository
public class EmployeeRepository:BaseRepository<EmployeeModel>,IEmployeeRepository
{
public EmployeeRepository():base()
{
}
public EmployeeRepository(SPWeb web):base(web)
{
}
public IEnumerable<EmployeeModel> GetEmployeeByCountry(string country)
{
SPWeb web = _web ?? SPContext.Current.Web;
SPList list = web.Lists.TryGetList("Employee");
IEnumerable<EmployeeModel> employeeEntitiesList = null;
if (list!=null)
{
SPQuery query = new SPQuery();
query.ViewFields = string.Concat("<FieldRef Name='Title'/>", "<FieldRef Name='CountryField'/>");
query.ViewFieldsOnly = true;
if (!string.IsNullOrEmpty(country))
{
query.Query = @"<Where>
<Eq>
<FieldRef Name='CountryField'/>
<Value Type='Lookup'>" + country + @"</Value>
</Eq>
</Where>";
}
else
{
query.Query = "";
}
SPListItemCollection employeeListColl = list.GetItems(query);
employeeEntitiesList = GetEntities(employeeListColl);
}
return employeeEntitiesList;
}
protected override EmployeeModel GetEntity(SPListItem item)
{
return new EmployeeModel() {
Name = item["Title"].ToString(),
Country = item["CountryField"].ToString()
};
}
}
关键点来了,在Feature中向SharePoint Service Locator依赖注册(IRepositoy/Repositoy)
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPSite site = properties.Feature.Parent as SPSite;
IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent(site);
IServiceLocatorConfig serviceLocatorConfig = serviceLocator.GetInstance<IServiceLocatorConfig>();
serviceLocatorConfig.Site = site;
serviceLocatorConfig.RegisterTypeMapping<IEmployeeRepository, EmployeeRepository>();
}
//Uncomment the method below to handle the event raised before a feature is deactivated.
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPSite site = properties.Feature.Parent as SPSite;
IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent(site);
IServiceLocatorConfig serviceLocatorConfig = serviceLocator.GetInstance<IServiceLocatorConfig>();
serviceLocatorConfig.Site = site;
serviceLocatorConfig.RemoveTypeMappings<IEmployeeRepository>();
}