c# - 使用现有数据库的 Azure 移动应用程序

标签 c# azure azure-sql-database azure-mobile-services

我是第一次尝试使用 Azure 移动应用程序,我应该将其连接到现有且已填充的 SQL Azure 数据库。据我了解,必须向表中添加 VersionCreatedAtUpdatedAtDeleted 列,最重要的是必须将 id 列设置为身份。

问题是,在某些表上我已经有一个标识列(例如ItemID),我无法在不破坏连接到数据的现有第三方应用程序的情况下对其进行重命名。

问题是:有什么方法可以避免使用身份 Id 字段,也许可以通过某种方式映射原始身份?

[编辑] 我已经在网上查看了示例,如下所示:

https://blogs.msdn.microsoft.com/azuremobile/2014/05/22/tables-with-integer-keys-and-the-net-backend/

但是移动服务移动应用之间似乎存在一些差异,如果有人指出我正确的方向,我真的很高兴,也许是一个可用的例子

最佳答案

经过一些调整,示例成功了!

以此处的教程为起点:

https://blogs.msdn.microsoft.com/wsdevsol/2014/07/17/walkthrough-attaching-an-azure-sql-database-to-your-net-backend/

这是我所做的:

  1. *在我的表格中添加了 ITableData 必填字段:

    [Version] ROWVERSION NOT NULL,
    [CreatedAt] DATETIMEOFFSET (7) DEFAULT (sysutcdatetime()) NOT NULL,
    [UpdatedAt] DATETIMEOFFSET (7) NULL,
    [Deleted] BIT DEFAULT ((0)) NOT NULL

  2. 使用来自数据库的 EF Code First

  3. 创建模型
  4. 创建了仅包含数据字段并继承自 EntityData 的 DTO 类

  5. *创建自定义 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 命名空间。

  6. *创建了与示例中完全相同的 SQL 实用程序类

    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;
        }
    }
    

    我决定将其放入单独的“实用程序”文件中

  7. *在 Startup.MobileApp.cs 文件中创建映射,因为示例中提到的 WebApiConfig.cs 在移动应用中不存在。 Automapper 初始化代码按原样工作,我将其放在 ConfigureMobileApp 函数中的 HttpConfiguration config = new HttpConfiguration(); 之后。供引用:

    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帖子不同的地方。 希望有人觉得这有帮助!

关于c# - 使用现有数据库的 Azure 移动应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36036455/

相关文章:

c# - 比较通用字段

c# - 在模型级别定义具有唯一键的实体

azure - 允许设备数据的子集通过

sql-server - Azure - 数据未显示在 Easy Table 上

t-sql - SQL Azure : Deadlock on concurrent delete operations

windows-phone-7 - 从 WP7 连接到 SQL Azure

c# - SQLite typeof() 也应该与 SQL 一起工作吗?

c# - 如何反转 foreach 中的列表?

entity-framework - 部署到 Azure 时不运行 EF Code First 迁移

java - 将 RAMDirectory 上传到 AzureCloud 会创建 EOF 异常