【.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 = ; 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 = ,
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 || == 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 || == 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 genericParameters))
{
isMatch = true;
break;
}
}
} if (isMatch && !genericParameters.Any())
{
genericParameters = type.GetGenericArguments();
}
return isMatch;
} private bool IsTypeOf(Type type, Type interfaceDefinition)
{
bool isMatch = false;
if (type.BaseType != null)
{
isMatch = IsTypeOf(type.BaseType, interfaceDefinition);
}
if (!isMatch && interfaceDefinition.IsInterface && type.GetInterfaces().Any())
{
foreach (var i in type.GetInterfaces())
{
if (IsTypeOf(i, interfaceDefinition))
{
isMatch = true;
break;
}
}
} return isMatch;
}
}
//定义抽象基类,实现两个接口。
public abstract class EFRepository<TEntity> : IEntityRepository, IRepository<TEntity> where TEntity : class
{
private IEFRepositoryContext mobjContext = null; public IRepositoryContext Context
{
get { return mobjContext; }
} public EFRepository(string contextName = "Default")
{
IRepositoryContext context = ServiceLocator.Instance.GetService<IRepositoryContext>(contextName) ;
if (context is IEFRepositoryContext)
{
mobjContext = context as IEFRepositoryContext;
}
else
{
//
throw new NotSupportedException();
}
} public void Add(TEntity entity)
{
mobjContext.Add<TEntity>(entity);
} public void Add(IEnumerable<TEntity> entities)
{
foreach (TEntity entity in entities)
{
Add(entity);
}
} public void Update(TEntity entity)
{
mobjContext.Update<TEntity>(entity);
} public void Update(IEnumerable<TEntity> entities)
{
foreach (TEntity entity in entities)
{
Update(entity);
}
} public void DeleteByKey(params object[] keyValues)
{
TEntity defaultEntity = this.FindByKeyValues(keyValues);
if (defaultEntity != null)
mobjContext.Delete<TEntity>(defaultEntity);
} public void Delete(TEntity entity)
{
mobjContext.Delete<TEntity>(entity);
} public void Delete(IEnumerable<TEntity> entities)
{
foreach (TEntity entity in entities)
{
Delete(entity);
}
} /// <summary>
///
/// </summary>
/// <param name="expression"></param>
/// <param name="includePath">to get related data at one time, EF use latency loading as default</param>
/// <returns></returns>
public IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> expression = null, params string[] includePath)
{
IQueryable<TEntity> defaultQuery = mobjContext.Context.Set<TEntity>();
if (includePath != null)
{
foreach (string path in includePath)
{
if (!string.IsNullOrEmpty(path))
{
defaultQuery = defaultQuery.Include(path);
}
}
} if (expression != null)
defaultQuery = defaultQuery.Where(expression);
return defaultQuery;
} public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> expression = null, params string[] includePath)
{
IQueryable<TEntity> defaultQuery = Query(expression, includePath);
return defaultQuery.FirstOrDefault();
} public TEntity FindByKeyValues(params object[] keyValues)
{
return mobjContext.Context.Set<TEntity>().Find(keyValues);
} public IList<TEntity> FindList(Expression<Func<TEntity, bool>> expression = null, params string[] includePath)
{
IQueryable<TEntity> defaultQuery = Query(expression, includePath);
return defaultQuery.ToList();
}
public IList<TEntity> FindDistinctList(Expression<Func<TEntity, bool>> expression = null, params string[] includePath)
{
IQueryable<TEntity> defaultQuery = Query(expression, includePath);
return defaultQuery.Distinct().ToList();
} public IList<TEntity> FindListByOrder<TKey>(Expression<Func<TEntity, bool>> expression = null, Expression<Func<TEntity, TKey>> orderBy = null, bool ascending = true, params string[] includePath)
{
IQueryable<TEntity> defaultQuery = Query(expression, includePath);
if (orderBy != null)
{
if (ascending)
defaultQuery = defaultQuery.OrderBy(orderBy);
else
defaultQuery = defaultQuery.OrderByDescending(orderBy);
} return defaultQuery.ToList();
} public 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)
{
IQueryable<TEntity> defaultQuery = Query(expression, includePath);
if (orderBy != null)
{
if (ascending)
defaultQuery = defaultQuery.OrderBy(orderBy);
else
defaultQuery = defaultQuery.OrderByDescending(orderBy);
}
count = defaultQuery.Count();
defaultQuery = defaultQuery.Skip(pageIndex).Take(pageSize); return defaultQuery.ToList();
} public IList<TEntity> SqlQueryList(string sqlQueryScript, params object[] parameters)
{
return mobjContext.Context.Set<TEntity>().SqlQuery(sqlQueryScript, parameters).ToList();
} public IEnumerable<object> QueryEntities(string sqlQueryScript, params object[] parameters)
{
ServerLogger.Info(string.Format("Query entity by sql {0}", sqlQueryScript));
return SqlQueryList(sqlQueryScript, parameters);
}
}
//实例化EF上下文操作接口
public abstract class EFRepositoryContext : IEFRepositoryContext
{
protected abstract System.Data.Entity.DbContext GetContext(); public System.Data.Entity.DbContext Context
{
get
{
return GetContext();
}
} public virtual void Initialize()
{
GetContext();
} public virtual void Add<T>(T entity) where T : class
{
if (Context != null)
{
Context.Set<T>().Add(entity);
}
else
{
ServerLogger.Warn("Missing DB Context");
}
} public virtual void Update<T>(T entity) where T : class
{
if (Context != null)
{
Context.Set<T>().Attach(entity);
Context.Entry<T>(entity).State = System.Data.Entity.EntityState.Modified;
}
else
{
ServerLogger.Warn("Missing DB Context");
}
} public virtual void Delete<T>(T entity) where T : class
{
if (Context != null)
{
Context.Set<T>().Remove(entity);
}
else
{
ServerLogger.Warn("Missing DB Context");
}
} public virtual void Save()
{
if (Context != null)
{
Context.SaveChanges();
}
else
{
ServerLogger.Warn("Missing DB Context");
}
} public virtual void BeginTransaction()
{
if (Context != null && Context.Database.CurrentTransaction == null)
{
ServerLogger.Info("Begin Transaction");
Context.Database.BeginTransaction();
ServerLogger.Info("Transaction started");
}
} public virtual void Commit()
{
if (Context != null && Context.Database.CurrentTransaction != null)
{
ServerLogger.Info("Start to Commit");
Context.Database.CurrentTransaction.Commit();
ServerLogger.Info("Committed");
}
} public virtual void Rollback()
{
if (Context != null && Context.Database.CurrentTransaction != null)
{
ServerLogger.Info("Start to rollback");
Context.Database.CurrentTransaction.Rollback();
ServerLogger.Info("Rollback");
}
} public virtual void Dispose()
{
try
{
if (Context != null)
{
if (Context.Database.CurrentTransaction != null)
{
Context.Database.CurrentTransaction.Dispose();
} if (Context.Database.Connection.State != System.Data.ConnectionState.Closed)
{
Context.Database.Connection.Close();
}
Context.Dispose();
}
}
catch(Exception ex)
{
ServerLogger.Error("Faile to dispose DB context", ex);
}
}
}
四、.Infrastructure.Mn与.Module.Mn
.Infrastructure.Mn
当我们完成.Infrastructure项目之后就可以开始写其它模块了。
.Infrastructure.Mn需要引用.Infrastructure与Database.EFModel项目。
最简单的形式如下:
public interface IModuleOneRepository : IRepository<ModuleOneItem>
{
//ModuleOneItem为EF的ORM对象!
}
public interface IModuleOneService
{
void functionOne();
void functionTwo();
}
.Module.Mn
.Module.Mn需要引用.Infrastructure、Database.EFModel以及.Infrastructure.Mn项目
最简单的形式如下:
public class ModuleOneRepository : EFRepository<ModuleOneItem>, IModuleOneRepository
{
public ModuleOneRepository(): base()
{
}
}
using Microsoft.Practices.Unity; public class ModuleOneService:BaseModuleService, IModuleOneService
{
[Dependency]
public IModuleOneRepository moduleOneRepository { get; set; } public void functionOne(){}
public void functionTwo(){}
} //[Dependency]是Unity依赖注入的属性注入标签
五、结语
本篇主要叙述Web项目所需要引用的CoreService相关项目,有了业务核心底层dlls,剩下的就可以在Web项目中进行使用了。Web项目主要使用.Net MVC模式,在我进入项目组时,MVC框架层进行过多次修改。当我有一次拆分完一个模块的js代码时,我和另一位Tech Leader以及我们的总架构师都意识到需要进一步优化我们的MVC框架。下篇将带来Web项目的MVC架构方案以及我们是如何引用本篇的CoreService相关项目!
【.NET架构】BIM软件架构02:Web管控平台后台架构的更多相关文章
- IT咨询顾问:一次吐血的项目救火 java或判断优化小技巧 asp.net core Session的测试使用心得 【.NET架构】BIM软件架构02:Web管控平台后台架构 NetCore入门篇:(十一)NetCore项目读取配置文件appsettings.json 使用LINQ生成Where的SQL语句 js_jquery_创建cookie有效期问题_时区问题
IT咨询顾问:一次吐血的项目救火 年后的一个合作公司上线了一个子业务系统,对接公司内部的单点系统.我收到该公司的技术咨询:项目启动后没有规律的突然无法登录了,重新启动后,登录一断时间后又无法重新登 ...
- 【.Net架构】BIM软件架构03:Web管控平台MVC架构
一.前言 上一篇讲述的是将BIM平台后台架构CoreService.sln,该解决方案主要作用是对管控平台的核心业务进行封装,然后让前端的ApiController去调用该解决方案中的对 ...
- 【.NET架构】BIM软件架构01:Revit插件产品架构梳理
一.前言 BIM:Building Information Modeling 建筑信息模型,就是将建筑的相关信息附着于模型中,以管理该建筑在设计.算量.施工.运维全生命周期的情况.创建模 ...
- KingbaseFlySync V1R6 管控平台Linux命令行安装
关键字: KingbaseFlySync.KingbaseES.Linux.x86_64.mips64el.aarch64.Java 管控平台: Web管控平台(Manager.Console.Com ...
- 从服务端架构设计角度,深入理解大型APP架构升级
随着智能设备普及和移动互联网发展,移动端应用逐渐成为用户新入口,重要性越来越突出.但企业一般是先有PC端应用,再推APP,APP 1.0版的功能大多从现有PC应用平移过来,没有针对移动自身特点考虑AP ...
- Web应用的组件化(二)——管控平台 #7
Web应用的组件化(二) 管控平台 在上一篇中我们提到了组件化的大致思路,这一篇主要讲述在这么做之后,我们需要哪些外围手段去管控整个开发过程.从各种角度看,面对较大规模前端开发团队,都有必要建立这么一 ...
- 朱晔的互联网架构实践心得S2E7:漫谈平台架构的工作(基础架构、基础服务、基础平台、基础中间件等等)
前言 程序开发毕竟还不是搬砖这种无脑体力劳动,需要事先有标准,有架构,有设计,绝对不是新公司今天创立,明天就可以开始编码的.其实很多公司在起步的时候没有财力和资源建设独立的基础架构或平台架构部门,甚至 ...
- AI中台——智能聊天机器人平台的架构与应用(分享实录)
内容来源:宜信技术学院第3期技术沙龙-线上直播|AI中台——智能聊天机器人平台 主讲人:宜信科技中心AI中台团队负责人王东 导读:随着“中台”战略的提出,目前宜信中台建设在思想理念及架构设计上都已经取 ...
- 基于Wiki的知识共享平台模型架构
一.引言 当今的全球化知识经济社会中呈现出信息泛滥和知识更新周期短的现象,知识管理逐渐成为现代企业管理中不容忽视的一环.虚拟企业是基于共识目标而组成的动态协作组织,成员参与的流动性与各成员之间地域分布 ...
随机推荐
- Ajax原生四大步骤
1.首先创建一个js文件夹名为common.js.创建一个createXhr()的函数.在此方法中创建异步对象XMLHttpRequest,后面使用的时候直接引入common.js文件,然后进行调用就 ...
- Easyui form 表单中的 radio 无法一开始就选中原因
是因为你在form表单里面,所以会出现样式没有选中状态,必须要从后台去获取数据,然后再绑定.也就是初始化数据.
- KiB和KB的区别
原文链接:http://blog.csdn.net/starshine/article/details/8226320 原来没太注意MB与MiB的区别,甚至没太关注还有MiB这等单位,今天认真了一下, ...
- kbmmw 中虚拟文件操作入门
kbmmw 中一直有一个功能,但是基本上都没有提过,但是在实际应用中,却非常有用,这个功能就是 虚拟文件包功能,他可以把一大堆文件保存到一个文件里面,方便后台管理. kbmmw 的虚拟文件在单元kbm ...
- windows mysql绿色版配置
MySQL绿色版安装 1.下载地址 https://dev.mysql.com/downloads/mysql/ 2.配置my.ini 文件 解压下载文件到指定目录.如: my.ini文件内容: [m ...
- HMM(隐马尔可夫模型)不断学习中
HMM(隐马尔可夫模型)是用来描述隐含未知参数的统计模型,举一个经典的例子:一个东京的朋友每天根据天气{下雨,天晴}决定当天的活动{公园散步,购物,清理房间}中的一种,我每天只能在twitter上看到 ...
- 查阅Springboot官方文档方式----------------Springboot2.0.2最新稳定版
1.登录官方网址: https://spring.io/ 如图所示: 2.选择PROJECTS,就可以看到spring所有的相关项目了. 点开后:其中就包括了Spingboot 3.版本选择,红圈部分 ...
- hadoop yarn组件介绍
Yarn的产生 mapReduc1.0 1单点故障 2扩展效率低 3资源利用率高 降低运维成本 方便数据共享 多计算框架支持 MapReduce Spark Storm Yarn的架构图 Yarn模块 ...
- mmm和mmma的区别
m:编译整个安卓系统 makes from the top of the tree mm:编译当前目录下的模块,当前目录下需要有Android.mk这个makefile文件,否则就往上找最近的Andr ...
- BT656与BT1120的区别
从ITU-R BT1120文档上可知,BT1120支持的是1080p: 文档定义一帧为1 125 总行数和1 080 有效行:每行有效像素为1920图像频率60. 50. 30. 25 和 24H ...