使用现有数据库的 Azure 移动应用程序
Azure Mobile App using existing database
我是第一次尝试使用 Azure 移动应用程序,我应该将它连接到现有且已经填充的 SQL Azure 数据库。据我所知,必须在表中添加 Version、CreatedAt、UpdatedAt 和已删除 列,最重要的是必须将 id 列设置为标识。
问题是在某些表上我已经有一个标识列(例如 ItemID),我无法在不破坏连接到数据的现有第三方应用程序的情况下重命名。
问题是:有什么方法可以避免使用身份 Id 字段,也许可以映射原始身份?
[编辑]
我已经看过网上的例子了,像这样:
但移动服务和移动应用程序之间似乎存在一些差异,如果有人指出我,我将非常高兴朝着正确的方向前进,也许有一个可用的例子
经过一些改编,示例成功了!
以这里的教程为起点:
这是我所做的:
*添加到我的 table ITableData 必填字段:
[Version] ROWVERSION NOT NULL,
[CreatedAt] DATETIMEOFFSET (7) DEFAULT (sysutcdatetime()) NOT NULL,
[UpdatedAt] DATETIMEOFFSET (7) NULL,
[Deleted] BIT DEFAULT ((0)) NOT NULL
使用 EF 代码优先从数据库创建模型
创建了仅包含数据字段并继承自 EntityData
的 DTO class
*像这样创建自定义 MappingDomaninManager:
public class MSSEntityDomainManager<TData, TModel>
: MappedEntityDomainManager<TData, TModel>
where TData : class, ITableData, new()
where TModel : class
{
private Expression<Func<TModel, object>> dbKeyProperty;
public MSSEntityDomainManager(MssContext context,
HttpRequestMessage request,
Expression<Func<TModel, object>> dbKeyProperty):base(context, request)
{
this.dbKeyProperty = dbKeyProperty;
}
public override Task<bool> DeleteAsync(string id)
{
return this.DeleteItemAsync(ConvertId(id));
}
public override SingleResult<TData> Lookup(string id)
{
return this.LookupEntity(GeneratePredicate(id));
}
public override Task<TData> UpdateAsync(string id, Delta<TData> patch)
{
return await this.UpdateEntityAsync(patch, ConvertId(id));
}
private static Expression<Func<TModel, bool>> GeneratePredicate(string id)
{
var m = Mapper.FindTypeMapFor<TModel, TData>();
var pmForId = m.GetExistingPropertyMapFor(new AutoMapper.Impl.PropertyAccessor(typeof(TData).GetProperty("Id")));
var keyString = pmForId.CustomExpression;
var predicate = Expression.Lambda<Func<TModel, bool>>(
Expression.Equal(keyString.Body, Expression.Constant(id)),
keyString.Parameters[0]);
return predicate;
}
private object ConvertId(string id)
{
var m = Mapper.FindTypeMapFor<TData, TModel>();
var keyPropertyAccessor = GetPropertyAccessor(this.dbKeyProperty);
var pmForId = m.GetExistingPropertyMapFor(new AutoMapper.Impl.PropertyAccessor(keyPropertyAccessor));
TData tmp = new TData() { Id = id };
var convertedId = pmForId.CustomExpression.Compile().DynamicInvoke(tmp);
return convertedId;
}
private PropertyInfo GetPropertyAccessor(Expression exp)
{
if (exp.NodeType == ExpressionType.Lambda)
{
var lambda = exp as LambdaExpression;
return GetPropertyAccessor(lambda.Body);
}
else if (exp.NodeType == ExpressionType.Convert)
{
var convert = exp as UnaryExpression;
return GetPropertyAccessor(convert.Operand);
}
else if (exp.NodeType == ExpressionType.MemberAccess)
{
var propExp = exp as System.Linq.Expressions.MemberExpression;
return propExp.Member as PropertyInfo;
}
else {
throw new InvalidOperationException("Unexpected expression node type: " + exp.NodeType);
}
}
}
与原始示例的不同之处在于从构造函数中完全删除了 ApiServices 引用,并添加了 AutoMapper.Impl 命名空间到 PropertyAccessor,否则默认使用 System.Web.Http.OData一个.
*创建了 SQL 实用程序 class 与示例中的完全一样
public static class MySqlFuncs
{
[DbFunction("SqlServer", "STR")]
public static string StringConvert(long number)
{
return number.ToString();
}
[DbFunction("SqlServer", "LTRIM")]
public static string LTRIM(string s)
{
return s == null ? null : s.TrimStart();
}
// Can only be used locally.
public static long LongParse(string s)
{
long ret;
long.TryParse(s, out ret);
return ret;
}
}
这个我决定放在一个单独的 "utility" 文件中
*在 Startup.MobileApp.cs 文件中创建映射,因为示例中提到的 WebApiConfig.cs 在 Mobile 中不存在应用程序。 Automapper 初始化代码按原样工作,我将其放在 HttpConfiguration config = new HttpConfiguration(); 之后 ConfigureMobileApp 函数中。供参考:
AutoMapper.Mapper.Initialize(cfg =>
{
// Mapping from database type to client type
cfg.CreateMap<StuffToGet, StuffToGetDto>()
.ForMember(dst => dst.Id, map => map.MapFrom(src => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(src.ID))));
// Mapping from client type to database type
cfg.CreateMap<StuffToGetDto, StuffToGet>()
.ForMember(dst => dst.ID, map => map.MapFrom(src => MySqlFuncs.LongParse(src.Id)));
});
标有“*”的是与原msdn不同的地方post。
希望有人觉得有用!
我是第一次尝试使用 Azure 移动应用程序,我应该将它连接到现有且已经填充的 SQL Azure 数据库。据我所知,必须在表中添加 Version、CreatedAt、UpdatedAt 和已删除 列,最重要的是必须将 id 列设置为标识。
问题是在某些表上我已经有一个标识列(例如 ItemID),我无法在不破坏连接到数据的现有第三方应用程序的情况下重命名。
问题是:有什么方法可以避免使用身份 Id 字段,也许可以映射原始身份?
[编辑] 我已经看过网上的例子了,像这样:
但移动服务和移动应用程序之间似乎存在一些差异,如果有人指出我,我将非常高兴朝着正确的方向前进,也许有一个可用的例子
经过一些改编,示例成功了!
以这里的教程为起点:
这是我所做的:
*添加到我的 table ITableData 必填字段:
[Version] ROWVERSION NOT NULL,
[CreatedAt] DATETIMEOFFSET (7) DEFAULT (sysutcdatetime()) NOT NULL,
[UpdatedAt] DATETIMEOFFSET (7) NULL,
[Deleted] BIT DEFAULT ((0)) NOT NULL使用 EF 代码优先从数据库创建模型
创建了仅包含数据字段并继承自 EntityData
的 DTO class
*像这样创建自定义 MappingDomaninManager:
public class MSSEntityDomainManager<TData, TModel> : MappedEntityDomainManager<TData, TModel> where TData : class, ITableData, new() where TModel : class { private Expression<Func<TModel, object>> dbKeyProperty; public MSSEntityDomainManager(MssContext context, HttpRequestMessage request, Expression<Func<TModel, object>> dbKeyProperty):base(context, request) { this.dbKeyProperty = dbKeyProperty; } public override Task<bool> DeleteAsync(string id) { return this.DeleteItemAsync(ConvertId(id)); } public override SingleResult<TData> Lookup(string id) { return this.LookupEntity(GeneratePredicate(id)); } public override Task<TData> UpdateAsync(string id, Delta<TData> patch) { return await this.UpdateEntityAsync(patch, ConvertId(id)); } private static Expression<Func<TModel, bool>> GeneratePredicate(string id) { var m = Mapper.FindTypeMapFor<TModel, TData>(); var pmForId = m.GetExistingPropertyMapFor(new AutoMapper.Impl.PropertyAccessor(typeof(TData).GetProperty("Id"))); var keyString = pmForId.CustomExpression; var predicate = Expression.Lambda<Func<TModel, bool>>( Expression.Equal(keyString.Body, Expression.Constant(id)), keyString.Parameters[0]); return predicate; } private object ConvertId(string id) { var m = Mapper.FindTypeMapFor<TData, TModel>(); var keyPropertyAccessor = GetPropertyAccessor(this.dbKeyProperty); var pmForId = m.GetExistingPropertyMapFor(new AutoMapper.Impl.PropertyAccessor(keyPropertyAccessor)); TData tmp = new TData() { Id = id }; var convertedId = pmForId.CustomExpression.Compile().DynamicInvoke(tmp); return convertedId; } private PropertyInfo GetPropertyAccessor(Expression exp) { if (exp.NodeType == ExpressionType.Lambda) { var lambda = exp as LambdaExpression; return GetPropertyAccessor(lambda.Body); } else if (exp.NodeType == ExpressionType.Convert) { var convert = exp as UnaryExpression; return GetPropertyAccessor(convert.Operand); } else if (exp.NodeType == ExpressionType.MemberAccess) { var propExp = exp as System.Linq.Expressions.MemberExpression; return propExp.Member as PropertyInfo; } else { throw new InvalidOperationException("Unexpected expression node type: " + exp.NodeType); } } }
与原始示例的不同之处在于从构造函数中完全删除了 ApiServices 引用,并添加了 AutoMapper.Impl 命名空间到 PropertyAccessor,否则默认使用 System.Web.Http.OData一个.
*创建了 SQL 实用程序 class 与示例中的完全一样
public static class MySqlFuncs { [DbFunction("SqlServer", "STR")] public static string StringConvert(long number) { return number.ToString(); } [DbFunction("SqlServer", "LTRIM")] public static string LTRIM(string s) { return s == null ? null : s.TrimStart(); } // Can only be used locally. public static long LongParse(string s) { long ret; long.TryParse(s, out ret); return ret; } }
这个我决定放在一个单独的 "utility" 文件中
*在 Startup.MobileApp.cs 文件中创建映射,因为示例中提到的 WebApiConfig.cs 在 Mobile 中不存在应用程序。 Automapper 初始化代码按原样工作,我将其放在 HttpConfiguration config = new HttpConfiguration(); 之后 ConfigureMobileApp 函数中。供参考:
AutoMapper.Mapper.Initialize(cfg => { // Mapping from database type to client type cfg.CreateMap<StuffToGet, StuffToGetDto>() .ForMember(dst => dst.Id, map => map.MapFrom(src => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(src.ID)))); // Mapping from client type to database type cfg.CreateMap<StuffToGetDto, StuffToGet>() .ForMember(dst => dst.ID, map => map.MapFrom(src => MySqlFuncs.LongParse(src.Id))); });
标有“*”的是与原msdn不同的地方post。 希望有人觉得有用!