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:
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
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 连接也很方便
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
.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 > ());
好进入重点,接下来实验下服务器到客服端的元数据转换是怎么样的。
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
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 的标记属性字段。我们加上后,成功了!所在的客户端会自动生成一堆文件代码:(请看下图)
我的的实体据模型肯定会有很多关联组合情况。我们扩展下:
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
[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 ; }
}
我们编译后看看客户端到底是什么:
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/// 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值。好了,我们看看完整的结果吧:
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
[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 来映射很方便,我们在实体类同目录下添加以下代码。我们就直接上代码了。
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
// 如果没有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]标记。
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 方式进行传输数据方式 实现对不同数据库的操作了。
写到这里,我想也有必要介绍下自定义用户验证,下一节我们再介绍吧,休息。。。