Silverlight下用Ria Services访问多种数据库
Ria Services 与 linq to Sql, Ado.net Entity Framework 的集合很紧密,但现目前不能支持其他数据库的操作(如:Oracle,my SQL等……),而现在支持多数据库的ORM: DataObjects.Net不错,对Linq的支持最完整的一个ORM了,但是现目前不支持.net framwork 4.0,官方好像正在支持中。。。看了很久,现目前还是只能用NHibernate了,FluentNHibernate为何物,大家可以去官方网站看看http://fluentnhibernate.org/。
好,那么我们尝试用FluentNHibernate来实现Ria Service,多数据库访问。我们就用Oracle来做实验咯:)
添加一些dll
首先来建立连接Session:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
private static ISessionFactory createSessionFactory()
{
FluentConfiguration fluentCfg = Fluently.Configure()
.Database(
OracleDataClientConfiguration
.Oracle10
.ConnectionString(
c => c.Server(ConfigurationManager.AppSettings["DBHost"])//127.0.0.1")
.Instance(ConfigurationManager.AppSettings["DBInstance"])
.Username(ConfigurationManager.AppSettings["DBUser"])
.Password(ConfigurationManager.AppSettings["DBPwd"])
).QuerySubstitutions("true=1,false=0"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Department>());
ISessionFactory factory = null;
try
{
factory = fluentCfg.BuildSessionFactory();
}
catch (Exception ex)
{
log.Error(ex);
throw new Exception("创建数据库连接出错。",ex);
}
return factory;
}
MS SQL 连接也很方便
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
FluentConfiguration fluentSqlCfg = Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2008.ConnectionString(
cn=>cn.Server(ConfigurationManager.AppSettings["DBHost"])//127.0.0.1")
.Database(ConfigurationManager.AppSettings["DBInstance"])
.Username(ConfigurationManager.AppSettings["DBUser"])
.Password(ConfigurationManager.AppSettings["DBPwd"])
).QuerySubstitutions("true=1,false=0"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Department>());
好进入重点,接下来实验下服务器到客服端的元数据转换是怎么样的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Department
{
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
[EnableClientAccess]
public class OrganizationService : DomainService
{
[Query]
public IQueryable<Department> GetAllDepartment()
{
return new List<Department>().AsQueryable();
}
public void AddDepartment(Department depart)
{
}
public void UpdateDepartment(Department depart)
{
}
public void DeleteDepartment(Department department)
{
}
}
编译时会出错,提示为:
原来必须要生有一个KeyAttribute 的标记属性字段。我们加上后,成功了!所在的客户端会自动生成一堆文件代码:(请看下图)
我的的实体据模型肯定会有很多关联组合情况。我们扩展下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Department
{
[Key]
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public virtual Department Parent { get; set; }
}
public class Person
{
[Key]
public int Id { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public String Code { get; set; }
public String Name { get; set; }
public Department Department { get; set; }
}
我们编译后看看客户端到底是什么:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary>
/// The 'Department' entity class.
/// </summary>
[DataContract(Namespace="http://schemas.datacontract.org/2004/07/Platform.Web.Models")]
public sealed partial class Department : Entity
{
private string _code;
private int _id;
private string _name;
#region Extensibility Method Definitions
/// <summary>
/// This method is invoked from the constructor once initialization is complete and
/// can be used for further object setup.
/// </summary>
partial void OnCreated();
partial void OnCodeChanging(string value);
partial void OnCodeChanged();
partial void OnIdChanging(int value);
partial void OnIdChanged();
partial void OnNameChanging(string value);
partial void OnNameChanged();
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="Department"/> class.
/// </summary>
public Department()
{
this.OnCreated();
}
/// <summary>
/// Gets or sets the 'Code' value.
/// </summary>
[DataMember()]
public string Code
{
get
{
return this._code;
}
set
{
if ((this._code != value))
{
this.OnCodeChanging(value);
this.RaiseDataMemberChanging("Code");
this.ValidateProperty("Code", value);
this._code = value;
this.RaiseDataMemberChanged("Code");
this.OnCodeChanged();
}
}
}
/// <summary>
/// Gets or sets the 'Id' value.
/// </summary>
[DataMember()]
[Editable(false, AllowInitialValue=true)]
[Key()]
[RoundtripOriginal()]
public int Id
{
get
{
return this._id;
}
set
{
if ((this._id != value))
{
this.OnIdChanging(value);
this.ValidateProperty("Id", value);
this._id = value;
this.RaisePropertyChanged("Id");
this.OnIdChanged();
}
}
}
/// <summary>
/// Gets or sets the 'Name' value.
/// </summary>
[DataMember()]
public string Name
{
get
{
return this._name;
}
set
{
if ((this._name != value))
{
this.OnNameChanging(value);
this.RaiseDataMemberChanging("Name");
this.ValidateProperty("Name", value);
this._name = value;
this.RaiseDataMemberChanged("Name");
this.OnNameChanged();
}
}
}
/// <summary>
/// Computes a value from the key fields that uniquely identifies this entity instance.
/// </summary>
/// <returns>An object instance that uniquely identifies this entity instance.</returns>
public override object GetIdentity()
{
return this._id;
}
}
/// <summary>
/// The 'Person' entity class.
/// </summary>
[DataContract(Namespace="http://schemas.datacontract.org/2004/07/Platform.Web.Models")]
public sealed partial class Person : Entity
{
private string _code;
private int _id;
private string _login;
private string _name;
private string _password;
#region Extensibility Method Definitions
/// <summary>
/// This method is invoked from the constructor once initialization is complete and
/// can be used for further object setup.
/// </summary>
partial void OnCreated();
partial void OnCodeChanging(string value);
partial void OnCodeChanged();
partial void OnIdChanging(int value);
partial void OnIdChanged();
partial void OnLoginChanging(string value);
partial void OnLoginChanged();
partial void OnNameChanging(string value);
partial void OnNameChanged();
partial void OnPasswordChanging(string value);
partial void OnPasswordChanged();
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="Person"/> class.
/// </summary>
public Person()
{
this.OnCreated();
}
/// <summary>
/// Gets or sets the 'Code' value.
/// </summary>
[DataMember()]
public string Code
{
get
{
return this._code;
}
set
{
if ((this._code != value))
{
this.OnCodeChanging(value);
this.RaiseDataMemberChanging("Code");
this.ValidateProperty("Code", value);
this._code = value;
this.RaiseDataMemberChanged("Code");
this.OnCodeChanged();
}
}
}
/// <summary>
/// Gets or sets the 'Id' value.
/// </summary>
[DataMember()]
[Editable(false, AllowInitialValue=true)]
[Key()]
[RoundtripOriginal()]
public int Id
{
get
{
return this._id;
}
set
{
if ((this._id != value))
{
this.OnIdChanging(value);
this.ValidateProperty("Id", value);
this._id = value;
this.RaisePropertyChanged("Id");
this.OnIdChanged();
}
}
}
/// <summary>
/// Gets or sets the 'Login' value.
/// </summary>
[DataMember()]
public string Login
{
get
{
return this._login;
}
set
{
if ((this._login != value))
{
this.OnLoginChanging(value);
this.RaiseDataMemberChanging("Login");
this.ValidateProperty("Login", value);
this._login = value;
this.RaiseDataMemberChanged("Login");
this.OnLoginChanged();
}
}
}
/// <summary>
/// Gets or sets the 'Name' value.
/// </summary>
[DataMember()]
public string Name
{
get
{
return this._name;
}
set
{
if ((this._name != value))
{
this.OnNameChanging(value);
this.RaiseDataMemberChanging("Name");
this.ValidateProperty("Name", value);
this._name = value;
this.RaiseDataMemberChanged("Name");
this.OnNameChanged();
}
}
}
/// <summary>
/// Gets or sets the 'Password' value.
/// </summary>
[DataMember()]
public string Password
{
get
{
return this._password;
}
set
{
if ((this._password != value))
{
this.OnPasswordChanging(value);
this.RaiseDataMemberChanging("Password");
this.ValidateProperty("Password", value);
this._password = value;
this.RaiseDataMemberChanged("Password");
this.OnPasswordChanged();
}
}
}
/// <summary>
/// Computes a value from the key fields that uniquely identifies this entity instance.
/// </summary>
/// <returns>An object instance that uniquely identifies this entity instance.</returns>
public override object GetIdentity()
{
return this._id;
}
}
客户端的Eitity模型好像少了关联类啊!!!怎么办,赶紧查资料吧。原来关联类需要加属性AssociationAttribute标记。。。加后编译也报错好需要添加一些字段,原来wcf Ria 从客户端传到服务器只传送是关联类实体Id值。好了,我们看看完整的结果吧:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Department
{
[Key]
public virtual int Id { get; set; }
[Display(GroupName = "基本信息", Name = "编码")]
public virtual string Code { get; set; }
[Display(GroupName = "基本信息", Name = "部门名称")]
public virtual string Name { get; set; }
/// <summary>
/// 是否是外部单位
/// </summary>
public virtual bool IsExtUnits { get; set; }
public virtual int ParentId { get; set; }
private Department _Parent;
/// <summary>
/// 上级部门或单位
/// </summary>
[Association("Department_Parent", "ParentId", "Id", IsForeignKey = true)]
[Display(GroupName = "基本信息", Name = "上级部门")]
[Include]
public virtual Department Parent
{
get
{
return _Parent;
}
set
{
ParentId = value == null ? 0 : value.Id;
_Parent = value;
}
}
/// <summary>
/// 下级部门或单位
/// </summary>
[Association("Department_Parent", "Id", "ParentId")]
public virtual IList<Department> Children { get; set; }
/// <summary>
/// 当前部门或单位的人员
/// </summary>
[Include]
[Association("Person_Department", "Id", "DepartmentId")]
public virtual IList<Person> Persons { get; set; }
}
public class Person
{
[Key]
public virtual int Id { get; set; }
[Display(GroupName = "基本信息", Name = "用户名")]
public virtual string Login { get; set; }
[Display(GroupName = "基本信息", Name = "密码")]
public virtual string Password { get; set; }
[Display(GroupName = "基本信息", Name = "工号")]
public virtual String Code { get; set; }
[Display(GroupName = "基本信息", Name = "姓名")]
public virtual String Name { get; set; }
#region 所在单位
public virtual int DepartmentId
{
get;
set;
}
private Department _department;
[Include]
[Association("Person_Department", "DepartmentId", "Id", IsForeignKey = true)]
[Display(GroupName = "基本信息", Name = "部门")]
public virtual Department Department
{
get
{
return _department;
}
set
{
this._department = value;
DepartmentId = value == null ? 0 : value.Id;
}
}
#endregion
}
大家看到实体属性都有了virtual来修饰的,因为用FluentNHibernate构建映射时需要。
现在看来从客户端到服务器之间的问题解决了,接下来该处理怎么将实体模型保存到数据库的问题了。
用Flunent 来映射很方便,我们在实体类同目录下添加以下代码。我们就直接上代码了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public DepartmentMap()
{
//如果没有Id值 自动生成唯一Id
Id(x => x.Id).GeneratedBy.HiLo("HIBERNATE_UNIQUE_KEY", "NEXT_HI", "1000");
Map(x => x.Code);
Map(x => x.Name).Not.Nullable();
Map(x => x.IsExtUnits);
//外键关联映射
References<Department>(x => x.Parent).Column("Parent_Id");
HasMany<Department>(x => x.Children).AsList().LazyLoad().KeyColumn("Parent_Id").Inverse();
//Department_Id 是在Person表里面的一个外键,
//.Inverse()这个方法可以确保主表保存后在保存子表
HasMany<Person>(x => x.Persons).KeyColumn("Department_Id").AsBag().Inverse();
}
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
//实体类对应数据库表名称
Table("Person");
Id(x => x.Id).GeneratedBy.HiLo("HIBERNATE_UNIQUE_KEY", "NEXT_HI", "1000");
Map(x => x.Code);
Map(x => x.Name);
Map(x => x.Login);
Map(x => x.Password);
//外键 Department_Id在Person表里
References<Department>(x => x.Department).Column("Department_Id");
//HasOne<UserProfile>(x => x.Profile).Cascade.All().PropertyRef(y=>y.User);
}
}
就这么简单,完成了实体模型到数据库的映射关系。用起来很方便吧。。。。(不过还是没有DataObjects.Net 实体定义方便,直接用Attribute就可以了),接下来我们来实现public class OrganizationService : DomainService 。记住服务类一定要加[EnableClientAccess]标记。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
[EnableClientAccess]
public class OrganizationService : DomainService
{
/// <summary>
/// 更新,插入都需要检查外接关联关系
/// </summary>
/// <param name="entity"></param>
internal static void EnsureFKValue(Object entity)
{
if (entity == null)
{
return;
}
ISession session = SessionHelper.GetSession();
if (session == null)
{
return;//log
}
string entityName = entity.GetType().FullName;
//session.SessionFactory.GetClassMetadata
Type entityType = entity.GetType();
PropertyInfo[] properties = entityType.GetProperties();
foreach (var p in properties)
{
object[] attributes = p.GetCustomAttributes(typeof(AssociationAttribute), true);
if (attributes == null || attributes.Length == 0)
{
continue;
}
AssociationAttribute asso = attributes[0] as AssociationAttribute;
if (asso.IsForeignKey)
{
PropertyInfo fkProp = entityType.GetProperty(asso.ThisKey);
if (fkProp == null)
{
continue;
}
int? fkId = fkProp.GetValue(entity, null) as int?;
if (!fkId.HasValue || fkId.Value <= 0)
{
continue;
}
//获取关联的对象
Object fkEntity = session.Get(p.PropertyType, fkId.Value);
if (fkEntity == null)
{
continue;//?
}
p.SetValue(entity, fkEntity, null);
}
else
{//?
}
}
}
[Query]
public IQueryable<Department> GetDeparments()
{
var query = SessionHelper.GetSession().Linq<Department>();
return query;
}
public void AddDepartment(Department department)
{
var session = SessionHelper.GetSession();
EnsureFKValue(department);
session.Save(department);
session.Flush();
}
public void UpdateDepartment(Department department)
{
var session = SessionHelper.GetSession();
EnsureFKValue(department);
session.Update(department);
session.Flush();
}
public void DeleteDepartment(Department department)
{
var session = SessionHelper.GetSession();
var item = session.Linq<Department>().FirstOrDefault(a => a.Id == department.Id);
if (item != null)
session.Delete(item);
session.Flush();
}
[Query]
public IQueryable<Person> GetAllPerson()
{
var query = SessionHelper.GetSession().Linq<Person>();
return query;
}
public void AddPerson(Person person)
{
var session = SessionHelper.GetSession();
EnsureFKValue(person);
session.Save(person);
session.Flush();
}
public void UpdatePerson(Person person)
{
var session = SessionHelper.GetSession();
EnsureFKValue(person);
session.Update(person);
session.Flush();
}
public void DeletePerson(Person person)
{
var session = SessionHelper.GetSession();
var item = session.Linq<Person>().FirstOrDefault(a => a.Id == person.Id);
if (item != null)
session.Delete(item);
session.Flush();
}
}
到此我们就可以用wcf Ria Service 方式进行传输数据方式 实现对不同数据库的操作了。
写到这里,我想也有必要介绍下自定义用户验证,下一节我们再介绍吧,休息。。。