使用现有数据库的Azure移动应用 [英] Azure Mobile App using existing database

查看:54
本文介绍了使用现有数据库的Azure移动应用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm at my first attempt in using Azure Mobile App, and I should connect it to an existing and already populated SQL Azure DB. As far as I understand, one must add to the tables the Version, CreatedAt, UpdatedAt, and Deleted columns, and most important there must be and id column set as identity.

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

The problem is that on some tables I already have an identity column (eg. ItemID), wich I cannot rename without breaking existing third party applications that connects to the data.

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

The question is: Is there any way to avoid using the identity Id field, maybe someway mapping the original identity?

我已经看了网上的样本,像这样:

[edit] I've already looked at the samples on the web, like that:

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

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

but there seems to exist some differences between mobile service and mobile app, and I'd really be happy if someone point me out to the right direction, maybe with an usable example

推荐答案

通过一些修改,示例成功了!

With some adaptations the sample worked!

以本教程为起点:

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

这是我所做的:

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

  1. *Added to my table the ITableData mandatory fields:

[版本] ROWVERSION NOT NULL,
[CreatedAt] DATETIMEOFFSET(7)默认(sysutcdatetime())非空,
[UpdatedAt] DATETIMEOFFSET(7)NULL,
[已删除]位默认((0))不为空

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

  • 首先使用 EF代码从数据库中创建模型

  • Created the model with EF Code First From database

    仅使用数据字段创建DTO类,并从EntityData继承

    Created the DTO class with only the data fields and inherited from EntityData

    *像这样创建自定义MappingDomaninManager:

    *Created the custom MappingDomaninManager like this:

    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 .

    The differences with the original sample are the complete removal of the ApiServices reference from the constructor, and the addition of the AutoMapper.Impl namespace to PropertyAccessor, otherwise by default it will use the System.Web.Http.OData one.

    *完全按照示例中的示例创建SQL实用程序类

    *Created the SQL utility class exactly like the one in the example

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

    我决定将这个文件放入一个单独的实用程序"文件

    This one I've decided to put in a separate "utility" file

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

    *Created the mapping in Startup.MobileApp.cs file, as the WebApiConfig.cs mentioned in the samples don't exist in the Mobile App. The Automapper initialize code is working as-is, and I've put it just after HttpConfiguration config = new HttpConfiguration(); in ConfigureMobileApp function. For reference:

    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帖子不同. 希望有人觉得有帮助!

    Marked with the "*" are the points that are different from the original msdn post. Hope someone found that helpful!

    这篇关于使用现有数据库的Azure移动应用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆