zoukankan      html  css  js  c++  java
  • EF 4.X Code First 如何调用存储过程

    类似的技术也是从网上学习的,以前也看到过更详细的技术文章,今日见有人发问,自己再杜撰一份,以充实下自己的技术博客。

    这是代码片断,实现 Code First 调用存储过程,期待这个功能在将来新版本中有简化的方式。

    第一步,有这样一个存储过程:

    View Code
    ALTER  PROCEDURE [dbo].[sp_get_goods]
    @all char(1),
    @productID int
    AS
    select goods_id,gs_name,gs_id,goods_no,name,spec,gwt,nwt,voldesc,qtyunit ,price
    --into t_product_temp
    from t_goods g


     

    第二步,定义好Class和Map,我通常用EF Power Tool生成

    Product.CS

    View Code
    using System;
    using System.Collections.Generic;
    using System.Data.Services.Common;

    namespace ERP.Domain
    {
    [DataServiceKey("ProductID")]
    public class Product
    {
    public int ProductID { get; set; }
           public string ProductNo { get; set; } 
            public string ProductName { get; set; } 
            public string ProductSpec { get; set; } 
            public decimal? GrossWeight { get; set; } 
            public decimal? NetWeight { get; set; } 
            public string VolumeDescribe { get; set; } 
            public string QuantityUnit { get; set; } 
            public decimal? Price  { get; set; } 
            public string Currency { get; set; } 
            public string CategoryName { get; set; } 
            public int? CategoryID { get; set; } 
        }
    }

    ProductMap.CS

    View Code
    using System;
    using System.Data.Entity.ModelConfiguration;
    using System.Data.Common;
    using System.Data.Entity;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using ERP.Domain;

    namespace ERP.Domain.Mapping
    {
    public class ProductMap : EntityTypeConfiguration<Product>
    {
    public ProductMap()
    {
    this.ToTable("t_product_temp");
    this.HasKey(t => t.ProductID);
    this.Property(t => t.ProductID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
    this.Property(t => t.ProductID).HasColumnName("goods_id");
    this.Property(t => t.ProductNo).HasColumnName("goods_no");
    this.Property(t => t.ProductName).HasColumnName("name");
    this.Property(t => t.ProductSpec).HasColumnName("spec");
    this.Property(t => t.GrossWeight).HasColumnName("gwt");
    this.Property(t => t.NetWeight).HasColumnName("nwt");
    this.Property(t => t.VolumeDescribe).HasColumnName("voldesc");
    this.Property(t => t.QuantityUnit).HasColumnName("qtyunit");
    this.Property(t => t.Price).HasColumnName("price");
    this.Property(t => t.Currency).HasColumnName("currency_no");
    this.Property(t => t.CategoryName).HasColumnName("gs_name");
    this.Property(t => t.CategoryID).HasColumnName("gs_id");
    }
    }
    }

    第三步:想办法执行context.Database.SqlQuery(mapCacheItem.MapType, sql, parameters)调用存储过程返回数据

    SqlQueryService.CS

    View Code
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data.Entity;
    using System.Reflection.Emit;
    using System.Reflection;
    using System.Collections;
    using System.Data.Entity.ModelConfiguration;

    namespace COM.Data
    {
    public class SqlQueryService
    {
    private DbContext context;
    public SqlQueryService(DbContext context)
    {
    this.context = context;
    }

    class MapCacheItem
    {
    //public EntityTypeConfiguration<T> ConfigurationMap{get;set;}
    public Type MapType {get;set;}
    public Dictionary<string,string> PropertyNameMap{get;set;}
    }

    static private Dictionary<string,MapCacheItem> MapCache = new Dictionary<string,MapCacheItem>();

    private MapCacheItem CreateMapCacheItem<T>(string typeName,EntityTypeConfiguration<T> map) where T : class,new()
    {
    dynamic mapDynamic = new AccessPrivateWrapper(map);
    dynamic m = new AccessPrivateWrapper(mapDynamic.Configuration);
    dynamic configList = m.PrimitivePropertyConfigurations;
    Dictionary<string, string> entityMap = new Dictionary<string, string>();
    Dictionary<string, Type> dynamicTypePropList = new Dictionary<string, Type>();
    foreach (var c in configList)
    {
    //System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive.PrimitivePropertyConfiguration
    //System.Data.Entity.ModelConfiguration.Configuration.PrimitivePropertyConfiguration p;

    dynamic config = new AccessPrivateWrapper(c);
    Type type = typeof(int);
    string pName = string.Empty;
    foreach (var configItem in config.Key)
    {
    PropertyInfo p = configItem as PropertyInfo;
    type = configItem.PropertyType;
    pName = p.Name;
    }
    dynamic value = new AccessPrivateWrapper(config.Value);
    var item = new { PropertyName = pName, ColumnName = value.ColumnName };
    //var type = typeof(T).GetProperty(item.PropertyName).DeclaringType;
    dynamicTypePropList.Add(item.ColumnName, type);
    entityMap.Add(item.ColumnName, item.PropertyName);
    }
    Type resultType = CreateDynamicType.Instance.CreateTempType(typeName, dynamicTypePropList);
    return new MapCacheItem() { MapType = resultType, PropertyNameMap = entityMap };
    }

    #region DynamicExecuteSql
    public IEnumerable<T> DynamicExecuteSql<T>(EntityTypeConfiguration<T> map, string sql, params object[] parameters) where T : class,new()
    {
    string mapTypeName = map.GetType().FullName;
    if (!MapCache.ContainsKey(mapTypeName))
    MapCache.Add(mapTypeName, this.CreateMapCacheItem(mapTypeName,map));
    MapCacheItem mapCacheItem = MapCache[mapTypeName];
    var queryList = context.Database.SqlQuery(mapCacheItem.MapType, sql, parameters);
    foreach (var item in queryList)
    {
    var entity = new T();
    EntityReflectService.Instance.CopyEntityProperty(item, entity, mapCacheItem.PropertyNameMap);
    yield return entity;
    }
    }


    #endregion

    }




    }

    第四步:这里的mapCacheItem.MapType是通过TypeBuilder依据ProductMap运行期创建的Type:

    CreateDynamicType.CS

    View Code
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.Reflection.Emit;

    namespace COM.Data
    {
    public class CreateDynamicType
    {
    private static CreateDynamicType instance;
    public static CreateDynamicType Instance
    {
    get
    {
    return instance ?? (instance = new CreateDynamicType());
    }
    }

    private Dictionary<string, Type> cacheList = new Dictionary<string,Type>();
    public Type CreateTempType(string typeName, Dictionary<string, Type> propertyList)
    {
    if (!cacheList.Keys.Contains(typeName))
    {
    var builder = this.CreateTypeBuilder("DynamicAssembly", "DynamicModule", "DynamicType");
    foreach (var item in propertyList)
    {
    this.CreateAutoImplementedProperty(builder, item.Key, item.Value);
    }

    cacheList.Add(typeName, builder.CreateType());
    }
    return cacheList[typeName];
    }


    private TypeBuilder CreateTypeBuilder(string assemblyName, string moduleName, string typeName)
    {
    TypeBuilder typeBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run)
    .DefineDynamicModule(moduleName)
    .DefineType(typeName, TypeAttributes.Public);
    typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
    return typeBuilder;
    }

    private void CreateAutoImplementedProperty(TypeBuilder builder, string propertyName, Type propertyType)
    {
    const string PrivateFieldPrefix = "m_";
    const string GetterPrefix = "get_";
    const string SetterPrefix = "set_";

    // Generate the field.
    FieldBuilder fieldBuilder = builder.DefineField(string.Concat(PrivateFieldPrefix, propertyName),propertyType, FieldAttributes.Private);

    // Generate the property
    PropertyBuilder propertyBuilder = builder.DefineProperty( propertyName, PropertyAttributes.HasDefault, propertyType, null);

    // Property getter and setter attributes.
    MethodAttributes propertyMethodAttributes =MethodAttributes.Public | MethodAttributes.SpecialName |MethodAttributes.HideBySig;

    // Define the getter method.
    MethodBuilder getterMethod = builder.DefineMethod(string.Concat(GetterPrefix, propertyName), propertyMethodAttributes, propertyType, Type.EmptyTypes);

    // Emit the IL code.
    // ldarg.0
    // ldfld,_field
    // ret
    ILGenerator getterILCode = getterMethod.GetILGenerator();
    getterILCode.Emit(OpCodes.Ldarg_0);
    getterILCode.Emit(OpCodes.Ldfld, fieldBuilder);
    getterILCode.Emit(OpCodes.Ret);

    // Define the setter method.
    MethodBuilder setterMethod = builder.DefineMethod(string.Concat(SetterPrefix, propertyName),propertyMethodAttributes, null, new Type[] { propertyType });

    // Emit the IL code.
    // ldarg.0
    // ldarg.1
    // stfld,_field
    // ret
    ILGenerator setterILCode = setterMethod.GetILGenerator();
    setterILCode.Emit(OpCodes.Ldarg_0);
    setterILCode.Emit(OpCodes.Ldarg_1);
    setterILCode.Emit(OpCodes.Stfld, fieldBuilder);
    setterILCode.Emit(OpCodes.Ret);

    propertyBuilder.SetGetMethod(getterMethod);
    propertyBuilder.SetSetMethod(setterMethod);
    }
    }
    }

    第五步:从ProductMap的映射关系,可以构造出适合存储过程返回数据的类型,我还要借助AccessPrivateWrapper类访问ProductMap:

    AccessPrivateWrapper.CS

    View Code
     using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Dynamic;

    namespace COM.Data
    {
    public class AccessPrivateWrapper : DynamicObject
    {

    /// <summary>
    /// The object we are going to wrap
    /// </summary>
    object _wrapped;

    /// <summary>
    /// Specify the flags for accessing members
    /// </summary>
    static BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance| BindingFlags.Static | BindingFlags.Public;

    /// <summary>
    /// Create a simple private wrapper
    /// </summary>
    public AccessPrivateWrapper(object o)
    {
    _wrapped = o;
    }

    /// <summary>
    /// Create an instance via the constructor matching the args
    /// </summary>
    public static dynamic FromType(Assembly asm, string type, params object[] args)
    {

    var allt = asm.GetTypes();
    var t = allt.First(item => item.Name == type);


    var types = from a in args
    select a.GetType();

    //Gets the constructor matching the specified set of args
    var ctor = t.GetConstructor(flags, null, types.ToArray(), null);

    if (ctor != null)
    {
    var instance = ctor.Invoke(args);
    return new AccessPrivateWrapper(instance);
    }

    return null;
    }

    /// <summary>
    /// Try invoking a method
    /// </summary>
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
    var types = from a in args
    select a.GetType();

    var method = _wrapped.GetType().GetMethod(binder.Name, flags, null, types.ToArray(), null);

    if (method == null)
    return base.TryInvokeMember(binder, args, out result);
    else
    {
    result = method.Invoke(_wrapped, args);
    return true;
    }
    }

    /// <summary>
    /// Tries to get a property or field with the given name
    /// </summary>
    public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
    {
    //Try getting a property of that name
    var prop = _wrapped.GetType().GetProperty(binder.Name, flags);

    if (prop == null)
    {
    //Try getting a field of that name
    var fld = _wrapped.GetType().GetField(binder.Name, flags);
    if (fld != null)
    {
    result = fld.GetValue(_wrapped);
    return true;
    }
    else
    return base.TryGetMember(binder, out result);
    }
    else
    {
    result = prop.GetValue(_wrapped, null);
    return true;
    }
    }

    /// <summary>
    /// Tries to set a property or field with the given name
    /// </summary>
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
    var prop = _wrapped.GetType().GetProperty(binder.Name, flags);
    if (prop == null)
    {
    var fld = _wrapped.GetType().GetField(binder.Name, flags);
    if (fld != null)
    {
    fld.SetValue(_wrapped, value);
    return true;
    }
    else
    return base.TrySetMember(binder, value);
    }
    else
    {
    prop.SetValue(_wrapped, value, null);
    return true;
    }
    }




    }
    }

    第六步:生成ProductList时,通过反射赋值,自己搞了个属性复制的功能类:

    EntityReflectService.CS

    View Code
        public class EntityReflectService
    {
    static private EntityReflectService instance;
    static public EntityReflectService Instance
    {
    get
    {
    return instance ?? (instance = new EntityReflectService());
    }
    }

    /// <summary>
    /// 复制属性,如果原始对象与目标对象的属性名一致,则复制
    /// </summary>
    /// <param name="from">原始对象</param>
    /// <param name="to">目标对象</param>
    public void CopyEntityProperty(object from, object to)
    {
    PropertyInfo[] propertyInfoFrom = this.GetTypePropertyInfo(from.GetType());
    PropertyInfo[] propertyInfoTo = this.GetTypePropertyInfo(to.GetType());
    var dictFrom = propertyInfoFrom.ToDictionary(p => p.Name, p => p);
    foreach (var property in propertyInfoTo)
    {
    if (property.PropertyType.IsSealed && property.PropertyType.IsSerializable && dictFrom.ContainsKey(property.Name))
    {
    Object propertyValue = dictFrom[property.Name].GetValue(from, null);
    if (property.GetSetMethod() != null)
    property.SetValue(to, propertyValue, null);
    }
    }
    }

    private Dictionary<Type, PropertyInfo[]> propertyInfoCache = new Dictionary<Type, PropertyInfo[]>();
    private PropertyInfo[] GetTypePropertyInfo(Type type)
    {
    if (!propertyInfoCache.ContainsKey(type))
    propertyInfoCache.Add(type, type.GetProperties());
    return propertyInfoCache[type];
    }

    /// <summary>
    /// 从对象to复制所有属性到对象from,属性名可能不同
    /// </summary>
    /// <param name="from">原始对象</param>
    /// <param name="to">目标对象</param>
    /// <param name="propertyCopy">列名到属性名的映射表</param>
    public void CopyEntityProperty(object from, object to, Dictionary<string, string> propertyCopy)
    {
    PropertyInfo[] propertyInfoTo = this.GetTypePropertyInfo(to.GetType());
    PropertyInfo[] propertyInfoFrom = this.GetTypePropertyInfo(from.GetType());
    var dictFrom = propertyInfoFrom.ToDictionary(p => p.Name, p => p);
    var dictTo = propertyInfoTo.ToDictionary(p => p.Name, p => p);
    foreach (var copy in propertyCopy)
    {
    Object propertyValue = dictFrom[copy.Key].GetValue(from, null);
    var property = dictTo[copy.Value];
    if (property.GetSetMethod() != null)
    property.SetValue(to, propertyValue, null);
    }
    }
    }
    }

    第七步:绕了一大圈,终于可以测试调用了,期待更轻量的调用存储过程的方式,性能还有待提升

    View Code
     [Test]
    public IEnumerable<Product> GetProductListBySP(bool isAll,int productID)
    {
    var s = new SqlQueryService(this.CurrentDataSource);
    var p = isAll ? "Y" : "N";
    var result = s.DynamicExecuteSql(new ERP.Domain.Mapping.ProductMap(), string.Format("exec sp_get_goods '{0}',{1}", p,productID));
    return result;
    }
  • 相关阅读:
    096实战 在windows下新建maven项目
    095实战 ETL的数据来源,处理,保存
    094实战 关于js SDK的程序,java SDK的程序
    093实战 Nginx日志切割,以及脚本上传nginx的切割日志
    092实战 数据收集(各种事件)
    091实战 Nginx配置(日志服务器中关于日志的产生)
    android64位机子兼容32位.so库文件
    给 Android 初学者的 Gradle 知识普及
    Android重力感应开发
    随笔之Android平台上的进程调度探讨
  • 原文地址:https://www.cnblogs.com/heguo/p/2424622.html
Copyright © 2011-2022 走看看