VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • C#教程之【.NET架构】BIM软件架构02:Web管控平台后

一、前言

       之前一篇叙述的是Revit插件(桌面软件)的软件架构,本篇将开始叙述Web项目的架构方案。今年一月在老东家加入BIM平台部门,为一些大型国家项目搭建BIM管控平台,业主使用管控平台可以实时了解各部门的施工状态(包括进度、现场管理、产值等等),将这些信息与WebGL三维模型中的构件相互关联就可以监控整个施工项目。

       我们知道,一个Web项目如果使用MVC的方式的话常常使用到ApiController去请求数据,并根据返回的数据进行页面的更新。由于平台项目属于大型项目,所以平台架构师将ApiController/Action函数里的核心业务逻辑抽出,放在另一个解决方案中,即CoreService.sln。Web项目只要引用CoreService里的dlls,并以Unity注入的方式使用即可。为了不会让大家看得太晕,本篇还是以CoreService.sln为主,如何调用的话会写在下一篇中。

 

二、架构简图

可以看到,整个项目是比较清晰的:

1. DataBase.EFModel就是EntityFramework进行ORM放置edmx系列文件的地方。

2. Common用于放置一些基本的操作函数。

复制代码
 using Microsoft.Practices.Unity;
 using Microsoft.Practices.Unity.Configuration;

 //容器注册的一些方法
 public class ServiceLocator : IServiceProvider
 {
        private readonly IUnityContainer mobjContainer = null;
        private static readonly ServiceLocator instance = new ServiceLocator();

        private ServiceLocator()
        {
            UnityConfigurationSection section = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
            mobjContainer = new UnityContainer();
            section.Configure(mobjContainer, "Default");
        }

        public static ServiceLocator Instance
        {
            get { return instance; }
        }

        public IUnityContainer GetContainer()
        {
            return mobjContainer;
        }

        public object GetService(Type serviceType)
        {
            if (IsRegistered(serviceType))
            {
                return mobjContainer.Resolve(serviceType);
            }
            else
            {
                ServerLogger.Warn(string.Format("Service type {0} is not registered", serviceType.ToString()));
                return null;
            }
        }

        public object GetService(Type serviceType, string name)
        {
            if (IsRegistered(serviceType, name))
            {
                return mobjContainer.Resolve(serviceType, name);
            }
            else
            {
                ServerLogger.Warn(string.Format("Service type {0} is not registered with name {1}", serviceType.ToString(), name));
                return null;
            }
        }

        public T GetService<T>()
        {
            if (IsRegistered<T>())
            {
                return mobjContainer.Resolve<T>();
            }
            else
            {
                Type type = typeof(T);
                ServerLogger.Warn(string.Format("Service type {0} is not registered", type.ToString()));
                return default(T);
            }
        }

        public T GetService<T>(string name)
        {
            if (IsRegistered<T>(name))
            {
                return mobjContainer.Resolve<T>(name);
            }
            else
            {
                Type type = typeof(T);
                ServerLogger.Warn(string.Format("Service type {0} is not registered with name {1}", type.ToString(), name));
                return default(T);
            }
        }

        public IEnumerable<T> GetServices<T>()
        {
            return mobjContainer.ResolveAll<T>();
        }

        private bool IsRegistered(Type serviceType, string name="")
        {
            if(string.IsNullOrEmpty(name))
            {
                return mobjContainer.IsRegistered(serviceType);
            }
            else
            {
                return mobjContainer.IsRegistered(serviceType, name);
            }
        }

        private bool IsRegistered<T>(string name = "")
        {
            if (string.IsNullOrEmpty(name))
            {
                return mobjContainer.IsRegistered<T>();
            }
            else
            {
                return mobjContainer.IsRegistered<T>(name);
            }
        }
 }
复制代码

 

复制代码
 using log4net;
 using log4net.Appender;
 using log4net.Core;
 using log4net.Layout;
 using log4net.Repository.Hierarchy;

 //Logger
 public class ServerLogger
 {
        private static ILog Log { get; set; }

        static ServerLogger()
        {
            try
            {
                string logFolder = CommonDefine.GetLogPath();
                if (!Directory.Exists(logFolder))
                {
                    Directory.CreateDirectory(logFolder);
                }
                Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository();

                PatternLayout patternLayout = new PatternLayout();
                patternLayout.ConversionPattern = "%date{yyyy-MM-dd HH:mm:ss.fff} %level %message%newline";
                patternLayout.ActivateOptions();

                RollingFileAppender roller = new RollingFileAppender();
                roller.AppendToFile = true;
                roller.File = logFolder + @"\server.log";
                roller.Layout = patternLayout;
                roller.MaxSizeRollBackups = 100;

                roller.RollingStyle = RollingFileAppender.RollingMode.Date;
                roller.StaticLogFileName = true;
                roller.ActivateOptions();
                hierarchy.Configured = true;

                Logger logger = hierarchy.GetLogger("IMLogger") as Logger;//Log as Logger;
                logger.Additivity = false;
                logger.Level = Level.All;
                logger.AddAppender(roller);

                Log = LogManager.GetLogger("IMLogger");
            }
            catch (Exception)
            {

            }
        }

        public static void Debug(string message)
        {
            if (CanLog(LogLevel.DEBUG))
            {
                Log.Debug(message);
            }
        }

        public static void Info(string message)
        {
            if (CanLog(LogLevel.INFO))
            {
                Log.Info(message);
            }
        }

        public static void Warn(string message)
        {
            if (CanLog(LogLevel.WARN))
            {
                Log.Warn(message);
            }
        }

        public static void Perfomance(Stopwatch watch, string actionName)
        {
            if (CanLog(LogLevel.PERFORMANCE))
            {
                if (watch.IsRunning)
                    watch.Stop();
                string message = string.Format(actionName + " consumes time {0}", watch.Elapsed.ToString());
                Log.Info(message);
            }
        }

        public static void Error(string message)
        {
            if (CanLog(LogLevel.ERROR))
            {
                Log.Error(message);
            }
        }

        /* Obsolete
        public static void Fatal(string message)
        {
            Log.Fatal(message);
        }
        */

        public static void Error(string message, Exception ex)
        {
            if (CanLog(LogLevel.ERROR))
            {
                Log.Error(message, ex);
            }
        }

        public static void SQL(string sqlScriptMesage)
        {
            if (CanLog(LogLevel.SQL))
            {
                Log.Info(sqlScriptMesage);
            }
        }

        private static bool CanLog(LogLevel level)
        {
            LogLevel levelConfig = GetLogLevel();
            return levelConfig >= level;
        }

        private static LogLevel GetLogLevel()
        {
            LogLevel level = LogLevel.ERROR;
            try
            {
                string logLevel = CommonDefine.GetLogLevel();
                level = (LogLevel)Enum.Parse(typeof(LogLevel), logLevel.ToUpper());
            }
            catch(Exception ex)
            {
                // Cannot use Error method to avoid stack overflow, use log tool directly
                Log.Error("Failed to parse log level setting", ex);
            }
            return level;
        }
}

public enum LogLevel
{
        ERROR = 1,
        WARN,
        INFO,
        SQL,
        PERFORMANCE,
        DEBUG
}
复制代码

3. Infrastructure与Module是一对,Infrastructure用于定义相关接口,而Module用于实现Infrastructure的接口。这也是本文重点介绍的。

 

三、.Infrastructure与.Core

.Infrastructure

.Infrastructure一般可以有4个文件夹:

1. DatabaseContext主要定义与EF相关的操作接口:

复制代码
//事务操作
public interface ITransactionProcessor
{
      void BeginTransaction();
      void Commit();
      void Rollback();
}
复制代码
复制代码
//CRUD
public interface IRepositoryContext : ITransactionProcessor, IDisposable
{
      void Initialize();
      void Add<T>(T entity) where T : class;
      void Update<T>(T entity) where T : class;
      void Delete<T>(T entity) where T : class;
      void Save();
}
复制代码
//最后定义EF上下文操作接口
public interface IEFRepositoryContext : IRepositoryContext
{
        DbContext Context { get; }
}

2. DataContracts主要定义一些业务内需要的数据结构,注意其不引用EF中的ORM对象

3. Repositories主要定义EF中ORM对象集合接口

复制代码
//定义ORM对象数据集合操作泛型接口,T必须为类
public interface IRepository<TEntity> where TEntity : class
{
        /// <summary>
        /// Return IQueryable without actually query DB 
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="includePath"></param>
        /// <returns></returns>
        IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> expression = null, params string[] includePath);

        /// <summary>
        /// Find the first object which mataches the expression
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        TEntity FirstOrDefault(Expression<Func<TEntity, bool>> expression = null, params string[] includePath);

        /// <summary>
        /// Finds an entity with the given primary key values.
        /// The ordering of composite key values is as defined in the EDM
        /// </summary>
        /// <param name="keyValues"></param>
        /// <returns></returns>
        TEntity FindByKeyValues(params object[] keyValues);

        /// <summary>
        /// 根据指定条件表达式得到数据查询列表
        /// if no expression, resurn all
        /// </summary>
        IList<TEntity> FindList(Expression<Func<TEntity, bool>> expression = null, params string[] includePath);

        IList<TEntity> FindDistinctList(Expression<Func<TEntity, bool>> expression = null, params string[] includePath);

        IList<TEntity> FindListByOrder<TKey>(Expression<Func<TEntity, bool>> expression = null, Expression<Func<TEntity, TKey>> orderBy = null, bool ascending = true, params string[] includePath);

        /// <summary>
        /// Add entity into DB context
        /// </summary>
        /// <param name="entity"></param>
        void Add(TEntity entity);

        /// <summary>
        /// Add a collection of entities
        /// </summary>
        /// <param name="entities"></param>
        void Add(IEnumerable<TEntity> entities);

        /// <summary>
        /// 修改实体
        /// </summary>
        /// <param name="entity">实体</param>
        void Update(TEntity entity);

        /// <summary>
        /// Update a collection of entities
        /// </summary>
        /// <param name="entities"></param>
        void Update(IEnumerable<TEntity> entities);

        /// <summary>
        /// Remove entity by key or keys
        /// </summary>
        /// <param name="keyValues"></param>
        void DeleteByKey(params object[] keyValues);

        /// <summary>
        /// Remove entity
        /// </summary>
        /// <param name="entity"></param>
        void Delete(TEntity entity);

        /// <summary>
        /// Remove a collection of entities
        /// </summary>
        /// <param name="entity"></param>
        void Delete(IEnumerable<TEntity> entities);

        /// <summary>
        /// 分页获取全部集合
        /// </summary>
        /// <param name="count">返回的记录总数</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页大小</param>
        /// <returns>集合</returns>
        IList<TEntity> LoadPageList<TKey>(out long count, int pageIndex, int pageSize, Expression<Func<TEntity, bool>> expression = null, Expression<Func<TEntity, TKey>> orderBy = null, bool ascending = true, params string[] includePath);

        IList<TEntity> SqlQueryList(string sqlQueryScript, params object[] parameters);
}

//定义一个通过Sql查询脚本与相关参数得到数据集合的接口
public interface IEntityRepository
{
    IEnumerable<object> QueryEntities(string sqlQueryScript, params object[] parameters);
}
复制代码

4. Service主要定义业务逻辑服务接口


 .Core

 Core项目主要是注册所有接口并且对Infrastructure中定义的接口进行实现。

复制代码
    public class ApplicationService
    {
        private static object mobjLock = new object();
        private static ApplicationService mobjInstance = new ApplicationService();
        public bool IsInitialized { get; set; }

        public static ApplicationService Instance
        {
            get
            {
                return mobjInstance;
            }
        }

        private ApplicationService()
        {
        }

        public void Initialize()
        {
            lock (mobjLock)
            {
                if (IsInitialized)
                    return;

                // Register all interfaces first
                IUnityContainer container = ServiceLocator.Instance.GetContainer();

                IResourceManagerUtils resourceManager = ServiceLocator.Instance.GetService<IResourceManagerUtils>();
                resourceManager.InitializeResource("Resource", "SharedResources", System.Globalization.CultureInfo.CurrentCulture, "TestResource");

                Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
                foreach (Assembly assembly in assemblies)
                {
                    // fix bug that GetTypes() of some assembly may throw exception
                    // only allow customized assebmly start with Platform.
                    string assebmlyName = assembly.GetName().Name.ToLower();

                    try
                    {
                        IEnumerable<Type> definedTypes = assembly.GetTypes().Where(t => t.IsClass && !t.IsInterface && !t.IsAbstract);
                        RegisterRepositories(definedTypes);
                    }
                    catch (Exception ex)
                    {
                        ServerLogger.Error(string.Format("Failed to load dll {0}", assebmlyName), ex);
                    }
                }

                IsInitialized = true;
            }
        }

        private void RegisterRepositories(IEnumerable<Type> definedTypes)
        {
            IUnityContainer container = ServiceLocator.Instance.GetContainer();

            Type repositoryInterface = typeof(IRepository<>);
            Type entityRepositoryInterface = typeof(IEntityRepository);

            foreach (Type type in definedTypes)
            {
                Type[] parentIntefaces = type.GetInterfaces();

                // Is IRepository<T>
                if (IsGenericTypeOf(type, repositoryInterface))
                {
                    Type parentInterface = GetParentGenericInterface(repositoryInterface, parentIntefaces);
                    if (parentInterface != null)
                    {
                        ServerLogger.Debug(string.Format("Regsiter type {0} to interface {1}", type.FullName, parentInterface.FullName));
                        container.RegisterType(parentInterface, type);
                    }
                }

                Attribute[] customAttributes = Attribute.GetCustomAttributes(type, false);
                if (customAttributes != null)
                {
                    EntityRepositoryAttribute entityRepositoryAtt = customAttributes.FirstOrDefault(a => a is EntityRepositoryAttribute) as EntityRepositoryAttribute;
                    if (entityRepositoryAtt != null)
                    {
                        string name = entityRepositoryAtt.EntityClassName;
                        if (!string.IsNullOrEmpty(entityRepositoryAtt.EntityClassName))
                        {
                            // Is IEntityRepository
                            if (parentIntefaces.Any(t => t == entityRepositoryInterface))
                            {
                                ServerLogger.Debug(string.Format("Regsiter type {0} to interface {1}", type.FullName, entityRepositoryInterface.FullName));
                                container.RegisterType(entityRepositoryInterface, type, name);
                            }
                        }
                    }
                }
            }
        }

        private Type GetParentGenericInterface(Type repositoryInterface, Type[] interfaces)
        {
            if (null == interfaces || 0 == interfaces.Count())
            {
                return null;
            }

            foreach (var type in interfaces)
            {
                if (type.IsGenericType &&
                    type.GetGenericTypeDefinition() == repositoryInterface.GetGenericTypeDefinition())
                {
                    continue;
                }
                if (IsGenericTypeOf(type, repositoryInterface))
                {
                    return type;
                }
            }
            return null;
        }

        private Type GetParentInterface(Type repositoryInterface, Type[] interfaces)
        {
            if (null == interfaces || 0 == interfaces.Count())
            {
                return null;
            }

            foreach (var type in interfaces)
            {
                if (IsTypeOf(type, repositoryInterface))
                {
                    return type;
                }
            }
            return null;
        }

        private bool IsGenericTypeOf(Type type, Type genericDefinition)
        {
            Type[] parameters = null;
            return IsGenericTypeOf(type, genericDefinition, out parameters);
        }

        private bool IsGenericTypeOf(Type type, Type genericDefinition, out Type[] genericParameters)
        {
            genericParameters = new Type[] { };
            if (!genericDefinition.IsGenericType)
            {
                return false;
            }

            var isMatch = type.IsGenericType && type.GetGenericTypeDefinition() == genericDefinition.GetGenericTypeDefinition();
            if (!isMatch && type.BaseType != null)
            {
                isMatch = IsGenericTypeOf(type.BaseType, genericDefinition, out genericParameters);
            }
            if (!isMatch && genericDefinition.IsInterface && type.GetInterfaces().Any())
            {
                foreach (var i in type.GetInterfaces())
                {
                    if (IsGenericTypeOf(i, genericDefinition, out