Sqlserver报错:该伙伴事务管理器已经禁止了它对远程/网络事务的支持_cuihengju8933的博客-CSDN博客

mikel阅读(31)

来源: Sqlserver报错:该伙伴事务管理器已经禁止了它对远程/网络事务的支持_cuihengju8933的博客-CSDN博客

SQLServer执行链接服务器的远程查询报错:该伙伴事务管理器已经禁止了它对远程/网络事务的支持

在远程主机没有开启网络DTC访问。
http://blog.csdn.net/apollokk/article/details/51543349

双方启动MSDTC服务

(1)在windows控制面版–>管理工具–>服务–>Distributed Transaction Coordinator–>属性–>启动
(2)在CMD下运行”net start msdtc”开启服务后正常。

管理工具-组件服务,全部勾选。

OK。
远程查询访问正常。
 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/22996654/viewspace-2146331/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/22996654/viewspace-2146331/

Lambda表达式select()和where()的区别 - 小海很友爱 - 博客园

mikel阅读(35)

来源: Lambda表达式select()和where()的区别 – 小海很友爱 – 博客园

新建一个控制台程序ConsoleApplication1

1、where()用法:必须加条件,且返回对象结果。

static void Main(string[] args)
{
string[] arrays={“asd”,”abc”,”bbb”,”ccc”};
var results = arrays.Where(a=>a.Contains(“b”));//必须加条件,返回对象

foreach(var da in results )
{
Console.WriteLine(da);
}

Console.WriteLine(“按任意键可退出!”);
Console.ReadKey();
}

结果:abc  bbb。

2、select()用法:(1)(a=>a.Value==”22″)加条件查询时,返回bool型结果;(2)(a=>a)没条件返回对象

(1)(a=>a.Value==”22″)加条件查询时,返回bool型结果

static void Main(string[] args)
{
string[] arrays={“asd”,”abc”,”bbb”,”ccc”};
var results = arrays.Select(a => a.Contains(“b”));//1、(a=>a.Value==”22″)加条件查询时,返回bool型结果

foreach(var da in results )
{
Console.WriteLine(da);
}

Console.WriteLine(“按任意键可退出!”);
Console.ReadKey();
}

结果:False  True  True  False

(2)(a=>a)没条件返回对象

static void Main(string[] args)
{
string[] arrays={“asd”,”abc”,”bbb”,”ccc”};
var results = arrays.Select(a => a);//1、(a=>a)没条件,返回所有对象;

foreach(var da in results )
{
Console.WriteLine(da);
}

Console.WriteLine(“按任意键可退出!”);
Console.ReadKey();
}

结果:asd  abc   bbb   ccc 

python3.6引入docx后 ,文件报错 moduleNotFoundError:No module named 'exceptions'_海棠花未眠的博客-CSDN博客

mikel阅读(29)

来源: python3.6引入docx后 ,文件报错 moduleNotFoundError:No module named ‘exceptions’_海棠花未眠的博客-CSDN博客

python3.x版本移除了exceptions模块,但是docx包中引用了该模块

安装最新版python-docx模块即可

以下总结两种常用安装第三方包的方法

方法一:

#1 pip install python-docx安装

pip会自动先安装python-docx的前置需求包lxml,但安装失败。

那么就先 安装 lmxl ,具体方法 cmd 输入 pip install lxml

或者通过下载lxml.whl文件进行安装。

新版本pip已支持wheel格式,如若你的pip版本不支持,那么升级你的pip版本 升级pip具体命令 :python -m pip install –upgrade pip

或者 用以下方法安装wheel。

首先安装whl包:pip install wheel

接着去开发者页面下载lxml的安装包:https://pypi.python.org/pypi/lxml。页面默认是最新版本。

命令行进入安装包所在目录,执行pip install 安装包名称

方法二:
1.下载 python_docx-0.8.6-py2.py3-none-any.whl 地址: http://www.lfd.uci.edu/~gohlke/pythonlibs/

3. 命令行输入pip install python_docx-0.8.6-py2.py3-none-any.whl 重新下载docx包,问题解决。
————————————————
版权声明:本文为CSDN博主「衣服架子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/huijiaaa1/article/details/80616842

string.Format 异常 输入字符串的格式不正确_zhl71199713的专栏-CSDN博客_输入字符串的格式不正确怎么解决

mikel阅读(31)

来源: string.Format 异常 输入字符串的格式不正确_zhl71199713的专栏-CSDN博客_输入字符串的格式不正确怎么解决

最近做一个项目,在使用string.Format对数据进行整理的时候,一直报错:输入字符串的格式不正确。

纠结了很久。最后终于查了下资料终于知道了问题所在。现在将这个问题记录一下:

 

在代码中,需要将结果以Json格式,输出。最开始报错,写的代码如下:

public void TEST()
{
try
{
string t = “Jim”;
string sresult = string.Format(“{name:\”{0}\”}”, t);//这里报错
Console.Write(sresult);
}
catch (Exception ex)
{
}
}

后来查找了一些资料之后,才知道问题所在。
C# 中使用类似 {0}, {1:yyyy-MM-dd} 这样的格式占位符,如果被格式化字符串本身包含 { 或者 } 怎么办呢?答案是:用两个 { 或者 }连写表示单个。

我的代码问题就出在这个地方。修改之后,就好了,修改后的代码如下:

public void TEST()
{
try
{
string t = “Jim”;
string sresult = string.Format(“{{name:\”{0}\”}}”, t);
Console.Write(sresult);
}
catch (Exception ex)
{
}
}
事实上,很多情况下带特殊含义的字符都是这样转义的:如,

C# 中,当字符串常量带 @ 前导的时候,用两个 ” 连写表示一个 ” (半角双引号)

public void TEST()
{
try
{
string sresult = @”this is “”Jim”””;
string tresult=@”this is ‘Jim'”;
string tresult = @”this is “Jim””;//错误
Console.Write(sresult);
}
catch (Exception ex)
{
}
}
SQL 字符串常量,用两个 ‘ 连写表示一个 ‘ (半角单引号)
DECLARE @str6 varchar(100)
SET @str6 = ‘My UserName is ”Jinglecat”.’
PRINT @str6 — My UserName is ‘Jinglecat’.
正则表达式中用,两个 $ 连写表示一个 $ (dollar)
————————————————
版权声明:本文为CSDN博主「zhl71199713」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhl71199713/article/details/19846571

json格式的字符串使用string.Format()方法报错:输入字符串的格式不正确 - 梨花驿路 - 博客园

mikel阅读(28)

来源: json格式的字符串使用string.Format()方法报错:输入字符串的格式不正确 – 梨花驿路 – 博客园

解决:把大括号转义一下就可以了啊,大括号的转义是两个{{  结尾是}}

 

 

今天看同事写的代码,发现他在使用string.format拼接类似json格式的数据时,大括号多了一对,感觉不对就查了查msdn,如下:

转义大括号

左大括号和右大括号被解释为格式项的开始和结束。因此,必须使用转义序列显示文本左大括号或右大括号。在固定文本中指定两个左大括号 (“{{“) 以显示一个左大括号 (“{“),或指定两个右大括号 (“}}”) 以显示一个右大括号 (“}”)。按照在格式项中遇到大括号的顺序依次解释它们。不支持解释嵌套的大括号。

解释转义大括号的方式会导致意外的结果。例如,考虑要显示一个左大括号、一个格式化为十进制数的数值和一个右大括号的格式项“{{{0:D}}}”。但是,实际是按照以下方式解释该格式项:

前两个左大括号 (“{{“) 被转义,生成一个左大括号。

之后的三个字符 (“{0:”) 被解释为格式项的开始。

下一个字符 (“D”) 将被解释为 Decimal 标准数值格式说明符,但后面的两个转义大括号 (“}}”) 生成单个大括号。由于得到的字符串 (“D}”) 不是标准数值格式说明符号,所以得到的字符串会被解释为用于显示字符串“D}”的自定义格式字符串。

最后一个大括号 (“}”) 被解释为格式项的结束。

显示的最终结果是字符串“{D}”。不会显示本来要格式化的数值。

在编写代码时,避免错误解释转义大括号和格式项的一种方法是单独格式化大括号和格式项。也就是说,在第一个格式化操作中显示文本左大括号,在下一操作中显示格式项的结果,然后在最后一个操作中显示文本右大括号。

设计模式如何提升 vivo 营销自动化业务扩展性 | 引擎篇01 - vivo互联网技术 - 博客园

mikel阅读(32)

来源: 设计模式如何提升 vivo 营销自动化业务扩展性 | 引擎篇01 – vivo互联网技术 – 博客园

在《vivo 营销自动化技术解密 |开篇》中,我们从整体上介绍了vivo营销自动化平台的业务架构、核心业务模块功能、系统架构和几大核心技术设计。

本次带来的是系列文章的第2篇,本文详细解析设计模式和相关应用如何帮助营销自动化业务提升系统扩展性,以及实践过程中的思考和总结。

一、引言

营销业务本身极具复杂多变性,特别是伴随着数字化营销蓬勃发展的趋势,在市场的不同时期、公司发展的不同阶段、面向不同的用户群体以及持续效果波动迭代,都会产生不同的营销策略决策。

当面对随时变化的业务场景时,系统的扩展性就显得非常重要。而在谈到系统设计扩展性的时候,总是首先会想到设计原则和设计模式。但设计模式不是银弹,并不能解决所有问题,它只是前人提炼总结出来的招式方法,需要开发者根据实际业务场景进行合理的选择、合适的变通,才能真正去解决实际场景中的问题,并总结形成自己的方法论。

那么接下来我们看看设计模式是如何帮助我们在营销策略引擎中提升系统扩展性的。

二、营销策略引擎

先简单介绍一下营销策略引擎:策略引擎是通过搭建可视化流程组件,定义各个流程节点,自动化执行活动业务流程,从而提供不同运营活动能力。其中核心活动业务流程主要包括三大部分:运营活动配置->运营活动审批->运营活动执行

  • 运营活动配置:运营人员在系统后台配置运营活动。包括活动名称、活动时间、触发条件、活动用户和具体推送渠道(如短信、微信、push推送等)。
  • 运营活动审批:品质/主管人员审批运营活动配置。审批流程涉及了活动审批节点和人员的配置,审批相关的回调操作配置。
  • 运营活动执行:系统自动化执行运营活动的过程。即具体的渠道如短信、微信、push等推送活动的任务执行下发流程,包括用户数据准备,数据下发推送和数据效果回收等。

三、设计模式具体应用

3.1 运营活动配置

3.1.1 工厂模式

具体场景

一般情况下,根据不同的用户和活动场景,运营借助数据分析会决策出不同的活动策略,比如需要创建短信推送策略、微信图文推送策略、App Push推送策略等。此时我们可以使用工厂模式,统一管理具体推送策略的创建。

模式分析

在GoF《设计模式:可复用面向对象软件的基础》中:工厂模式被分成了工厂方法和抽象工厂两类,而简单工厂模式(又称静态工厂模式)被看作是工厂方法的一种特例。不过由于简单工厂和工厂方法相对更简单和易于理解,代码可读性也更强,因此在实际项目中更加常用。

其中简单工厂的适用场景:

  • a.工厂类负责创建的对象比较少,工厂方法中的创建逻辑简单。
  • b.客户端无须关心创建具体对象的细节,仅需知道传入工厂类的类型参数。

而工厂方法的适用场景:

  • a.工厂类对象创建逻辑相对复杂,需要将工厂实例化延迟到其具体工厂子类中。
  • b.适合需求变更频繁的场景,可以利用不同的工厂实现类支持新的工厂创建方案,更符合开闭原则,扩展性更好。

典型代码示例

//抽象产品类
public abstract class Product {
    public abstract void method();
}
//具体的产品类 
class ProductA extends Product {
    @Override
    public void method() {
        //具体的执行逻辑
     }
}
//抽象工厂模板类
abstract class Factory<T> {
    abstract Product createProduct(Class<T> c);
}
//具体工厂实现类
class FactoryA extends Factory{
    @Override
    Product createProduct(Class c) {
        Product product = (Product) Class.forName(c.getName()).newInstance();
        return product;
    }
}

实际代码

/**
 * @author chenwangrong
 * 活动策略工厂类
 */
@Component
@Slf4j
public class ActivityStrategyFactory {
 
    /**
     * 获得渠道类型对应的策略
     *
     * @param channelType channelType
     * @return OperationServiceStrategy
     */
    public static ActivityStrategy getActivityStrategy(ChannelTypeEnum channelType) {
 
        ChannelTypeStrategyEnum channelTypeStrategyEnum = ChannelTypeStrategyEnum.getByChannelType(channelType);
        Assert.notNull(channelTypeStrategyEnum , "指定的渠道类型[channelType=" + channelType + "]不存在");
 
        String strategyName= channelTypeStrategyEnum.getHandlerName();
        Assert.notNull(strategyName, "指定的渠道类型[channelType=" + channelType + "未配置策略");
 
        return (ActivityStrategy)SpringContextHolder.getBean(handlerName);
    }
 
 
    public enum ChannelTypeStrategyEnum {
        /**
         * 短信渠道
         */
        SMS(ChannelTypeEnum.SMS, "smsActivityStrategy"),
        /**
         * 微信渠道
         */
        WX_NEWS(ChannelTypeEnum.WX, "wxActivityStrategy"),
        /**
         * push渠道
         */
        PUSH(ChannelTypeEnum.PUSH, "pushActivityStrategy"),;
 
        private final ChannelTypeEnum channelTypeEnum;
 
        private final String strategyName;
 
        ChannelTypeStrategyEnum (ChannelTypeEnum channelTypeEnum, String strategyName) {
            this.channelTypeEnum = channelTypeEnum;
            this.strategyName= strategyName;
        }
 
 
        public String getStrategyName() {
            return strategyName;
        }
 
        public static ChannelTypeStrategyEnum getByChannelType(ChannelTypeEnum channelTypeEnum) {
            for (ChannelTypeStrategyEnum channelTypeStrategyEnum : values()) {
                if (channelTypeEnum == channelTypeStrategyEnum.channelTypeEnum) {
                    return channelTypeStrategyEnum ;
                }
            }
            return null;
        }
    }
}

实践总结

在实际项目代码中我们采用的是简单工厂模式(静态工厂模式),实现时利用枚举(或者映射配置表)来保存渠道类型与具体策略实现类的映射关系,再结合Spring的单例模式,来进行策略类的创建。

相比于工厂方法模式,在满足业务的前提下,减少了工厂类数量,代码更加简单适用。

3.1.2 模板方法模式

具体场景

在创建不同类型运营活动策略的时候,可以发现除了保存具体活动渠道配置信息不一样之外,创建过程中很多操作流程是相同的:比如保存活动基本配置信息,审计日志上报,创建活动审批工单,创建完成后消息提醒等

原有实践

/**
 * 短信活动类
 *
 */
@Service
public class SmsActivityStrategy{
  
    /**
     * 执行渠道发送
     *
     * @param msgParam msgParam
     */
    public ProcessResult createActivity(ActParam param) {
         //保存活动基础信息
         saveActBaseConfig(param);
         //保存短信活动配置
         createSmsActivity(param);
         //审计日志上报 ...
         //创建活动审批工单 ...
         //消息通知 ...
         sendNotification(param);
    }
}
 
/**
 * Push活动类
 *
 */
@Service
public class PushActivityStrategy{
  
    /**
     * 执行渠道发送
     *
     * @param msgParam msgParam
     */
    public ProcessResult createActivity(ActParam param) {
         //保存活动基础信息
         saveActBaseConfig(param);
         //保存Push活动配置
         createChannelActivity(param);
         //审计日志上报 ...
         //创建活动审批工单 ...
         //消息通知 ...
         sendNotification(param);
    }
}
 
...

对于每种活动策略而言,这些操作都是必需的且操作流程都是固定的,所以可以将这些操作提取成公用的流程,此时就考虑到了模板方法模式。

模式分析

在GoF《设计模式:可复用面向对象软件的基础》:模板方法模式是在一个方法中定义一个算法骨架,并将某些步骤推迟到其子类中实现。模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤。

上面所指的“算法”,可以理解为业务逻辑,而‘’算法骨架“即是模板,包含‘’算法骨架“的方法就是模板方法,这也是模板方法模式名称的来源。

模板方法模式适用场景:业务逻辑由确定的步骤组成,这些步骤的顺序要是固定不变的,不同的具体业务之间某些方法或者实现可以有所不同。

实现时一般通过抽象类来定义一个逻辑模板和框架,然后将无法确定的部分抽象成抽象方法交由子类来实现,调用逻辑仍在抽象类中完成。

典型代码示例

//模板类
public abstract class AbstractTemplate {
 
//业务逻辑1
protected abstract void doStep1();
//业务逻辑2
protected abstract void doStep2();
 
//模板方法
public void templateMethod(){
     this.doStep1();
     //公共逻辑
       ......
     this.doStep2();
   }
}
 
//具体实现类1
public class ConcreteClass1  extends AbstractTemplate {
  //实现业务逻辑1
  protected void doStep1()
  {
     //业务逻辑处理
  }
 
  //实现业务逻辑2
  protected void doStep2()
  {
    //业务逻辑处理
   }
}
 
//具体实现类2
public class ConcreteClass2  extends AbstractTemplate {
  //实现业务逻辑1
  protected void doStep1()
  {
     //业务逻辑处理
  }
 
  //实现业务逻辑2
  protected void doStep2()
  {
    //业务逻辑处理
   }
}
 
// 调用类
public class Client {
 public static void main(String[] args)
  {
    AbstractTemplate class1=new ConcreteClass1();
    AbstractTemplate class2=new ConcreteClass2();
   //调用模板方法
    class1.templateMethod();
    class2.templateMethod();
   }
}

实际代码

/**
 * 活动创建模板类
 *
 * @author chenwangrong
 */
@Slf4j
public abstract class AbstractActivityTemplate{
 
    /**
     * 保存具体活动配置
     *
     * @param param 活动参数
     * @return ProcessResult 处理结果
     */
    protected abstract ProcessResult createChannelActivity(ActParam param);
 
    /**
     * 执行活动创建
     *
     * @param msgParam msgParam
     */
    public ProcessResult createActivity(ActParam param) {
         //保存活动基础信息
         saveActBaseConfig(param);
         //保存具体渠道配置
         createChannelActivity(param);
         //审计日志上报 ...
         //消息通知 ...
    }
}
 
/**
 * 短信活动类
 *
 */
@Service
public class SmsActivityStrategy extends AbstractActivityTemplate{
  
    /**
     * 创建短信渠道活动配置
     *
     * @param msgParam msgParam
     */
    public ProcessResult createChannelActivity(ActParam param) {
         //仅需要实现:保存短信活动配置
         createSmsActivity(param);    
    }
}
 
(其他渠道活动类似,此处省略)
 
 
// 调用类
public class Client {
 public static void main(String[] args)
  {
    AbstractActivityTemplate smsActivityStrategy=new SmsActivityStrategy();
    AbstractActivityTemplate pushActivityStrategy=new PushActivityStrategy();
 
    ActParam param = new ActParam();
 
   //调用具体活动实现类
    smsActivityStrategy.createActivity(param);
    pushActivityStrategy.createActivity(param);
   }
}

实践总结

模板方法模式有两大作用:复用和扩展。复用是指所有的子类可以复用父类中提供的模板方法的代码。扩展是指框架通过模板模式提供功能扩展点,让用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。

模板方法非常适用于有通用业务逻辑处理流程,同时又在具体流程上存在一定差异的场景,可以通过将流程骨架抽取到模板类中,将可变的差异点设置为抽象方法,达到封装不变部分,扩展可变部分的目的。

3.1.3 策略模式

具体场景

上述我们通过模板方法模式抽取出了公共流程骨架,但这里还存在一个问题:调用类仍需要明确知道具体实现类是哪个,实例化后才可进行调用。也就是每一次增加新的渠道活动时,调用方都必须修改调用逻辑,添加新的活动实现类的初始化调用,显然不利用业务的扩展性。

在创建运营活动过程中,不同类型的活动会对应着不同的创建流程,调用方只需要根据渠道类型来进行区分,而无需理会其中具体的业务逻辑。此时策略模式是一个比较好的选择。

模式分析

在GoF《设计模式:可复用面向对象软件的基础》中:策略模式定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的调用方。

典型代码示例

//策略接口定义
public interface Strategy {
    void doStrategy();
}
​
//策略具体实现类(多个)
public class StrategyA implements Strategy{
    @Override
    public void doStrategy() {
    }
}
​
//上下文操作类, 屏蔽高层模块对策略的直接访问
public class Context {
    private Strategy strategy = null;
​
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
 
    public void doStrategy() {
        strategy.doStrategy();
    }
}

实际代码

/**
 * 渠道活动创建策略接口
 *
 */
public interface ActivityStrategy {
 
    /**
     * 创建渠道活动配置
     *
     * @param param 活动参数
     * @return 
     */
    void createActivity(ActParam param);
}
 
/**
 * 活动模板类
 *
 */
@Slf4j
public abstract class AbstractActivityTemplate implements ActivityStrategy {
 
    /**
     * 抽象方法:具体渠道活动创建
     *
     */
    protected abstract ProcessResult createChannelActivity(ActParam param);
 
    @Override
    public ProcessResult createActivity(ActParam param) {
         //保存活动基础信息
         saveActBaseConfig(param);
         //保存具体渠道配置
         createChannelActivity(param);
         //审计日志上报 ...
         //消息通知 ...
    }
}
 
/**
 * 短信推送策略具体实现类
 *
 */
@Component
public class SmsChannelActivityStrategy extends AbstractActivityTemplate {
    @Override
    public void createChannelActivity(ActParam param) {
        //保存短信配置数据
    }
}
(其他渠道活动类似,此处省略)
 
 
/**
 * 策略调用入口
 *
 */
@Slf4j
@Component
public class ActivityContext {
 
   @Resource
   private ActivityStrategyFactory activityStrategyFactory ;
 
      public void create(ActParam param) {
            //通过前面的工厂模式的代码,获取具体渠道对应的策略类
            ActivityStrategy strategy = activityStrategyFactory.getActivityStrategy(param.ChannelType);
            //执行策略
            strategy.createActivity(param);
      }
}

实际编码过程中,我们加入了ChannelActivityStrategy作为渠道活动创建策略接口,并用模板类AbstractActivityTemplate实现该接口,同时结合工厂模式创建具体策略,至此将三种模式结合了起来

实践总结

策略模式在项目开发过程中经常用于消除复杂的if else复杂逻辑,后续如果有新的渠道活动时,只需要新增对应渠道的活动创建逻辑即可,可以十分便捷地对系统业务进行扩展。

在项目实践过程,经常会将工厂模式、模板方法模式和策略模式一起结合使用。模板方法模式进行业务流程公共骨架的抽取,策略模式进行具体子流程策略的实现和调用的封装,而工厂模式可以进行子流程策略的创建。

多种模式的结合使用可以充分发挥出各个模式的优势,达到真正提升系统设计扩展性的目的。

3.2 运营活动执行

3.2.1 状态模式

具体场景

在运营活动的执行过程中,会涉及活动状态的变更,以及变更前的条件检测和变更后的操作处理。与之相对应地,我们很容易就会想到状态模式。

模式分析

在 GoF 经典的《设计模式:可复用面向对象软件的基础》中:状态模式允许一个对象在其内部状态改变的时候改变其行为。

状态模式的作用就是分离状态的行为,通过维护状态的变化,来调用不同状态对应的不同功能。它们的关系可以描述为:状态决定行为。由于状态是在运行期被改变的,因此行为也会在运行期随着状态的改变而改变。

典型代码示例

/**
 * 状态模式
 * 抽象状态类
 * */
interface State {
    //状态对应的处理
    void handle()
}
 
  
//具体状态关现类
public  class ConcreteStateA implements  State {
    @Override
    public void handle() {
    }
}
 
public  class ConcreteStateB implements  State {
    @Override
    public void handle() {
    }
}
 
//环境类Context,访问入口
public class Context {
    //持有一个State类型的对象实例
    private State state;
 
    public void setState(State state) {
        this.state = state;
    }
      
    public void request() {
        //转调state来处理
        state.handle();
    }
}
 
public class Client {
    public static void main(String[] args){
        //创建状态
        State state = new ConcreteStateB();
        //创建环境
        Context context = new Context();
        //将状态设置到环境中
        context.setState(state);
        //请求
        context.request();
    }
}

实践总结

在实际软件项目开发中,业务状态不多且状态转移简单的场景, 可使用状态模式来实现;但如果是涉及的业务流程状态转移繁杂时,使用状态模式会引入非常多的状态类和方法,当状态逻辑有变更时,代码也会变得难以维护,此时使用状态模式并不十分适合。

而当流程状态繁多,事件校验和触发执行动作包含的业务逻辑比较复杂时,如何去实现呢?

这里我们必须停下来思考:使用设计模式只是解决实际问题的一种手段,但设计模式不是一把“万能的”锤子,需要清楚地了解到它的优势和不足。而这种问题场景下,业界已经有一个更通用的方案——有限状态机,通过更高层的封装,提供给业务更便捷的应用。

3.2.2 状态模式的应用——有限状态机

有限状态机(Finite-State Machine , 缩写:FSM),业界简称状态机。它亦是由事件状态动作 三大部分组成,三者的关系是:事件触发状态的转移,状态的转移触发后续动作的执行。状态机可以基于传统的状态模式硬编码来实现,也可以通过数据库/文件配置或者DSL的方式来保存状态及转移配置来实现(推荐)。

业界中也已涌现出了不少开源状态机的框架,比较常用的有Spring-statemachine(Spring官方提供) 、squirrel statemachine和阿里开源的cola-statemachine。

实际应用

在实际项目开发中,我们针对自身业务的特点:业务流程状态多,但是事件触发和状态变更动作相对简单,故而选择了无状态、更加轻量级的解决方案——基于开源的状态机实现思想进行开发。(关于状态机的实现和使用选型会在后续的文章中做进一步的分析,感兴趣的童鞋可以访问官网先做了解)。

实践代码

/**
 * 状态机工厂类
 */
public class StatusMachineEngine {
    private StatusMachineEngine() {
    }
    private static final Map<OrderTypeEnum, String> STATUS_MACHINE_MAP = new HashMap();
 
    static {
        //短信推送状态
        STATUS_MACHINE_MAP.put(ChannelTypeEnum.SMS, "smsStateMachine");
        //PUSH推送状态
        STATUS_MACHINE_MAP.put(ChannelTypeEnum.PUSH, "pushStateMachine");
        //......
    }
 
    public static String getMachineEngine(ChannelTypeEnum channelTypeEnum) {
        return STATUS_MACHINE_MAP.get(channelTypeEnum);
    }
 
   /**
     * 触发状态转移
     * @param channelTypeEnum
     * @param status 当前状态
     * @param eventType 触发事件
     * @param context 上下文参数
     */
    public static void fire(ChannelTypeEnum channelTypeEnum, String status, EventType eventType, Context context) {
        StateMachine orderStateMachine = StateMachineFactory.get(STATUS_MACHINE_MAP.get(channelTypeEnum));
        //推动状态机进行流转,具体介绍本期先省略
        orderStateMachine.fireEvent(status, eventType, context);
    }
 
/**
 * 短信推送活动状态机初始化
 */
@Component
public class SmsStateMachine implements ApplicationListener<ContextRefreshedEvent> {
 
    @Autowired
    private  StatusAction smsStatusAction;
    @Autowired
    private  StatusCondition smsStatusCondition;
 
    //基于DSL构建状态配置,触发事件转移和后续的动作
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        StateMachineBuilder<String, EventType, Context> builder = StateMachineBuilderFactory.create();
        builder.externalTransition()
                .from(INIT)
                .to(NOT_START)
                .on(EventType.TIME_BEGIN)
                .when(smsStatusAction.checkNotifyCondition())
                .perform(smsStatusAction.doNotifyAction());
        builder.externalTransition()
                .from(NOT_START)
                .to(DATA_PREPARING)
                .on(EventType.CAL_DATA)
                .when(smsStatusCondition.doNotifyAction())
                .perform(smsStatusAction.doNotifyAction());
        builder.externalTransition()
                .from(DATA_PREPARING)
                .to(DATA_PREPARED)
                .on(EventType.PREPARED_DATA)
                .when(smsStatusCondition.doNotifyAction())
                .perform(smsStatusAction.doNotifyAction());
        ...(省略其他状态)
        builder.build(StatusMachineEngine.getMachineEngine(ChannelTypeEnum.SMS));
    }
 
   //调用端
   public class Client {
     public static void main(String[] args){
          //构建活动上下文
          Context context = new Context(...);
         // 触发状态流转
          StatusMachineEngine.fire(ChannelTypeEnum.SMS, INIT, EventType.SUBMIT, context);
      }
   }
}

通过预定义状态转换流程的方式,实现ApplicationListener接口,在应用启动时将事件、状态转移条件和触发操作的流程加载到状态机工作内存中,由事件触发驱动状态机进行自动流转。

实践总结

实际场景中,不必强行套用设计模式,而是应当充分结合业务的特点,同时针对设计模式的优劣势,进行更加合适的选型或者进一步扩展。

3.3 自动化运营活动审批

3.3.1 设计模式的综合应用——工作流引擎

具体场景

为了做好品质和风险管控,活动创建需要加入审批环节,把控运营活动的发布执行,同时对于不同类型的运营活动,可能涉及的业务领域和部门各不相同,审批管控人员也不一样,需要配置相对应的审批关系。

此时需要做到:

  • a.审批流程全配置化,易修改和添加;
  • b.业务流程节点可自由编排,组件公用化;
  • c.流程数据持久化,审批过程数据需要进行操作监控。

针对这方面的需求,业界有一套通用的业务工具——工作流引擎。工作流引擎显然并不属于具体某一种设计模式的实现,它是涵盖了多种设计模式的组件应用。

不仅仅是审批功能,其实前面自动化营销流程引擎设计也同样是使用工作流引擎搭建流程组件

状态机 VS 工作流引擎

工作流引擎和状态机似乎存在非常多的相似之处,都可以通过定义流程的节点、转移条件和相应触发的操作来完成业务流程。如果只从适用场景的复杂性上看,状态机更适用于单维度的业务问题,能够清晰地描绘出所有可能的状态以及导致转换的事件,更加灵活轻便;而工作流引擎则更适合业务流程管理,解决如大型CRM复杂度更高的流程自动化问题,可以改善整体业务流程的效率。

在业界的工作流引擎中,比较著名的有Activiti和JBPM等。(关于状态机和工作流引擎的对比、开源工作流引擎的具体介绍和选型,以及如何自行开发构建一款基本的工作流引擎组件,同样是会在后续的文章中做进一步分析,本文由于主题和篇幅的原因暂不做详细介绍。)

在实际开发过程中,我们是基于开源的Activiti工作流引擎自研了一套简易版的工作流引擎,精简了许多相关的配置,只留下了核心流程操作和数据记录。

工作流引擎流程图:

实践总结

工作流引擎是涵盖了多种设计模式的应用组件,只有在复杂多变的业务场景中才需要应用,需要结合业务进行仔细评估。在合适的场景使用合适的解决方案,遵循系统架构设计的简单、合适、可演化原则,不过度设计。

四、总结

本文基于自动化营销的业务实践,分析介绍了工厂方法模式、模板方法模式、策略模式以及状态模式这四种模式在项目开发中的具体实现过程。也在单纯的模式之外介绍了状态机和工作流引擎这些涵盖了多种设计模式系统组件,并分享了过程中的选择和思考。

面对业务复杂多变的需求,需要时刻关注系统设计的复用性和可扩展性,而设计原则和设计模式可以在系统设计实现时给予我们方向性的指导,同时更需要根据实际业务场景进行合理的选择,合适的变通,不断完善自己的方法论。

后续我们将带来系列专题文章的其他内容,每一篇文章都会对里面的技术实践进行详尽解析,敬请期待。

作者:vivo互联网服务器团队-Chen Wangrong

分享 vivo 互联网技术干货与沙龙活动,推荐最新行业动态与热门会议。

领域驱动设计(DDD:Domain-Driven Design) - 解道Jdon

mikel阅读(35)

来源: 领域驱动设计(DDD:Domain-Driven Design) – 解道Jdon

领域驱动设计(DDD:Domain-Driven Design)

Eric Evans的“Domain-Driven Design领域驱动设计”简称DDD,Evans DDD是一套综合软件系统分析和设计的面向对象建模方法,本站Jdon.com是国内公开最早讨论DDD网站之一,可订阅DDD专题。初学者学习DDD可从研究本站Jdon框架的DDD应用源码开始,戳这里开始

过去系统分析和系统设计都是分离的,正如我们国家“系统分析师” 和“系统设计师” 两种职称考试一样,这样割裂的结果导致,需求分析的结果无法直接进行设计编程,而能够进行编程运行的代码却扭曲需求,导致客户运行软件后才发现很多功能不是自己想要的,而且软件不能快速跟随需求变化。

DDD则打破了这种隔阂,提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。见下面DDD与传统CRUD或过程脚本或者面向数据表等在开发效率上比较:

ddd

服务器后端发展三个阶段:

  1. UI+DataBase的两层架构,这种面向数据库的架构(上图table module )没有灵活性。
  2. UI+Service+DataBase的多层SOA架构,这种服务+表模型的架构易使服务变得囊肿,难于维护拓展,伸缩性能差,见这里讨论Spring Web 应用的最大败笔垂直切片的烟囱式故事已经一去不复返了
  3. DDD+SOA微服务的事件驱动的CQRS读写分离架构,应付复杂业务逻辑,以聚合模型替代数据表模型,以并发的事件驱动替代串联的消息驱动。真正实现以业务实体为核心的灵活拓展。

DDD革命性在于:领域模型准确反映了业务语言,而传统J2EE或Spring+Hibernate等事务性编程模型只关心数据,这些数据对象除了简单setter/getter方法外,没有任何业务方法,被比喻成失血模型,那么领域模型这种带有业务方法的充血模型到底好在哪里?

比赛Match为案例,比赛有“开始”和“结束”等业务行为,但是传统经典的方式是将“开始”和“结束”行为放在比赛的服务Service中,而不是放在比赛对象本身之中。我们不能因为用了计算机,用了数据库,用了框架,业务模型反而被技术框架给绑架,就像人虽然是由母亲生的,但是人的吃喝拉撒母亲不能替代,更不能以母爱名义肢解人的正常职责行为,如果是这样,这个人就是被母爱绑架了。

提倡充血模型,实际就是让过去被肢解被黑crack的业务模型回归正常,当然这也会被一些先入为主或被洗过脑的程序员看成反而不正常,这更是极大可悲之处。看到领域模型代码,就看到业务需求,没有翻译没有转换,保证软件真正实现“拷贝不走样”。

DDD最大的好处是:接触到需求第一步就是考虑领域模型,而不是将其切割成数据和行为,然后数据用数据库实现,行为使用服务实现,最后造成需求的首肢分离。DDD让你首先考虑的是业务语言,而不是数据。重点不同导致编程世界观不同。

DDD是解决复杂中大型软件的一套行之有效方式,在国外已经成为主流。DDD认为很多原因造成软件的复杂性,我们不可能避免这些复杂性,能做的是对复杂的问题进行控制。而一个好的领域模型是控制复杂问题的关键。领域模型的价值在于提供一种通用的语言,使得领域专家、产品经理和软件技术人员联系在一起,沟通无歧义。

DDD落地实现离不开Clean架构、六边形架构、 CQRS、Event Source几大大相关领域。下图是传统以数据库为中心的架构与使用DDD实现以领域为中心架构的区别。

DDD专门为解决复杂性而诞生,因此解决思路完全不同于传统的CRUD,但是DDD本身掌握起来并不会感觉复杂,从程序员角度看,DDD其实是研究将包含业务逻辑的ifelse语句放在哪里的学问。

DDD主要难点是领域发现和领域建模,万事开头难,除了DDD原著作提出领域统一语言外,目前用于领域发现的方法有:事件风暴、业务能力建模、领域讲故事、业务模型画布、示例映射、影响映射、Wardley Maps等,全球DDD社区为此做出主要贡献的人员名单(按Twitter名称排列):

    @ericevans0 创建了DDD
    • @ziobrando 发明了

事件风暴

    建模方法。
    @ntcoding 发明使用画布canvas 映射有界上下文方法。
    • @swardley 发明

WardleyMapping方法

    进行战略规划。
    • @mathiasverraes 提出了

事件溯

    源具体设计策略,提出复杂系统的仿真建模。
    • @gregyoung 提出

CQRS模式

    来分离模型。

重点资讯

  复杂软件设计之道:领域驱动设计全面解析与实战

  用事件风暴分解单体设计微服务 – capital

  函数式DDD架构入门 – SCOTT WLASCHIN

  DDD社区权威解读:领域驱动设计在2021年将会怎样?

 

教程与文章

板桥大话DDD
用大白话简单谈谈DDD的一些基础特点,只是扫盲!数据库SQL强人慎入

板桥DDD研究十年心得:《复杂软件设计之道:领域驱动设计全面解析与实战》
承蒙机械出版社厚爱

板桥:为什么DDD的Bounded Context翻译为”有界上下文”?

业务代码编程陷阱案例 – jaxenter
非常普遍的不恰当的编程方式,失血模型导致的陷阱

面向对象建模与数据库建模两种分析设计方法的比较
数据库驱动设计与对象建模是决定软件不同命运的两大派别,谁可以让软件更具有生命,维护拓展更方便?伸缩性更强?

面向对象与领域建模
据调查,目前有70%左右程序员是在使用OO语言编写传统过程化软件,缺乏完整的面向对象思维方法的教育和培训是基本根源,本文对软件开发中几个常见问题提出了独立的见解及尖锐的观点

Evans DDD 领域建模
如何提炼模型,而不是数据表,进而精化模型对象,使其能够反映领域概念基本本质是一个复杂过程,Evans DDD是2004年提出的具备革命性影响的软件思想。

实战DDD(Evans DDD:Domain-Driven Design领域驱动设计)
领域建模是一种艺术的技术,不是数学的技术,它是用来解决复杂软件快速应付变化的解决之道。

领域模型驱动设计(Evans DDD)之模型提炼

软件建模设计

如何从职责和协作中发现丰富的充血对象?
失血模型贫血模型是DDD最大敌人,如何根据SOLID原则GRASP原则设计业务行为?本文给出了DDD具体实践中一些具体细节,是和DDD配合一起进行面向对象分析设计的好方法。

业务模型统一描述
统一语言是DDD一个重要特征和重点。

DDD CQRS和Event Sourcing的案例:足球比赛
DDD + CQRS + Event Sourcing实现案例,结合代码与理论讲解。

集装箱车队系统的DDD案例
为上海某大型港口公司的运输系统实施的一个领域驱动设计DDD的实战咨询案例。

DDD仓储实现:Spring Data JDBC教程

不使用DDD的后果:为什么我们停止了向微服务的迁移?

使用DDD聚合发现隐藏的业务规则的案例分析:数据库事务的业务实现 

向领域驱动设计前进: 如何使用DDD实现从单体到微服务迁移打造业务平台或中台?

DDD+微服务大型案例:Uber如何从复杂的RPC微服务转向面向业务领域的微服务架构DOMA?

全球大型电商Shopify如何使用DDD实现单体架构的模块化?

最全面DDD微服务教程:SpringBoot + DDD + Apache Kafka实现最终一致性的教程与源码 – itnext

更多#DDD领域驱动设计专题、领域事件专题

DDD案例完整实现:本站开源Jivejdon 文档按这里

DDD领域驱动设计落地实践(十分钟看完,半小时落地) - 只会一点java - 博客园

mikel阅读(30)

来源: DDD领域驱动设计落地实践(十分钟看完,半小时落地) – 只会一点java – 博客园

一、引子

不知今年吹了什么风,忽然DDD领域驱动设计进入大家视野。该思想源于2003年 Eric Evans编写的“Domain-Driven Design领域驱动设计”简称DDD,Evans DDD是一套综合软件系统分析和设计的面向对象建模方法。刚好公司领导强力推荐这个,抱着学习的心态,耗时5个月,体验了一把:“DDD从入门到弃坑”。

二、思想

学习网站:https://www.jdon.com/ddd.html

书:领域驱动设计

2.1 服务器后端发展三个阶段

 

服务器后端发展三个阶段:

  1. 面向过程脚本:初始简单,业务复杂后,维护难度指数上升。–>基本不为主流使用
  2. 面向数据库表:初始难度中,业务复杂后,维护难度延迟后再指数上升。—>目前市面上主流
  3. 面向业务模型:DDD+SOA微服务的事件驱动的CQRS读写分离架构:应付复杂业务逻辑,以聚合模型替代数据表模型,以并发的事件驱动替代串联的消息驱动。真正实现以业务实体为核心的灵活拓展。初始难度高,业务复杂后,维护难度线性上升(已很不错)

2.2 DDD最大特点

DDD革命性在于:领域模型准确反映了业务语言,而传统微服务数据对象除了简单setter/getter方法外,没有任何业务方法,即失血模型,那么DDD领域模型就是充血模型(业务方法定义在实体对象中)

 

三、落地

3.1 领域模型设计

以渠道中心(一个微服务)作为例子来做领域模型设计,核心就是设计2个图,一个是战略设计图(宏观)  ,一个是战术设计图(细节)。

1.领域战略设计图

战略设计图是从一个限界上下文的角度出发去分析业务场景。主要是宏观上的核心域、子域、实体关系图。demo如下图:

2.领域战术设计图

战术设计图是从一个限界上下文的角度出发去分析业务场景。细化到核心业务字段、领域实体、值对象、领域服务、领域事件等等。基本上这个图画完,代码已经知道怎么写了。demo如下图:

3.2 技术实现

整体项目框架分层图如下所示:

如上图,4层典型DDD分层结构,

1.展现层:controller层。无业务逻辑

2.应用服务层:此层可以包含查询逻辑,但核心业务逻辑必须下沉到领域层。

3.领域服务层:业务在这里组装。仓储(资源库)接口在此层定义。

4.基础设施层:仓储(资源库)实现层+PO持久化层。

注:

1.简单查询不涉及业务,是可以直接从应用层直接穿透到PO查询,不需要经过domain层。如下图所示,DDD本身是不限制非业务类操作跨层调用的。

 

2.DTO是不能存在于domain层的,DDD设计不认为DTO是业务对象,entity才是。或者传值简单数据类型也是可以的。

3.2.1 服务调用问题

1.域内调用

领域内调用,随便调用,丝般顺滑。至于实现,可以由一个核心域的仓储实现层(第四层)去实现多个Repository接口。(比如这里A是核心域的实体名,B是支撑域、通用域等)

 2.跨域调用

 

 

跨域分为

  • 1.同上下文跨域:ACL层->Adapter适配器层→调用其它域的repository。—>不得已才使用,不推荐使用。
  • 推荐:1.使用领域事件 eventbus来做解耦(nest-eventbus使用

               2.考虑是否有可能合并为一个领域.

  • 2.跨上下文(肯定跨域):ACL层->Adapter适配器层->feign调用

3.2.2 包结构

包结构如下:

 展开包结构如下:

展现层:Controller,仅做接口的入口定义和编排转发,不做任何的业务处理;

应用服务层:application,负责接口参数DTO的简单校验,以及DTO和实体值对象的数据转换,对于简单的业务,也可以在应用层加载实体直接执行实体行为方法;

领域层:

  • 模型:根据领域模型分析领域内各实体、聚合、聚合根、值对象等,这些对象在*.domain.model定义,实体内的行为方法只负责维护实体自身的生命周期和状态;
  • 行为:领域内各实体、聚合、聚合根等,会有相应的行为,在*.domain.model包下定义行为方法;
  • 领域服务:领域提供的接口服务,需要定义在*.domain.service包下,业务相关的前置业务判断、多个实体或值对象的行为逻辑处理等,都在领域服务中实现,需要注意的是并不是每个实体都有一个对应的领域服务,但是依赖多个实体的行为方法,最好根据这个业务模块是建立一个领域服务;
  • 仓储:领域服务或上层应用服务需要使用到的基础设施层,包括DB、Feign调用等,定义在*.domain.repository下,在*.infrastructure.repository下实现;

适配层:在acl包下的feign定义依赖外部的接口,并在acl的adapter包编写转换,由仓储层操作实体时调用;

持久层:与常用DAO定义一致,由仓储层操作实体时调用。

 3.2.3 技术架构

目前业内没有标杆,github开源地址:https://github.com/jovezhao/nest  。作者不是本人哈,这个项目可以练手DDD。

 

四、总结

DDD可以尝试,但不建议主流业务硬上。建议浅尝即止。(据我所知,业内连阿里巴巴都不敢上。)

开源框架 - 新 代码生成器 WebFirst / .NET Core - 果糖大数据科技 - 博客园

mikel阅读(55)

来源: 开源框架 – 新 代码生成器 WebFirst / .NET Core – 果糖大数据科技 – 博客园

框架描述

WebFirst  是一新代的 代码生成器,用法简单,功能强大,支持多种数据库 ,具体功能如下:

一、 建库、CodeFirst方式在线建表,没用到CodeFirst的用户可以用工具轻松体验,支持公共字段

二、导出EXCEL文档,把每个表的数据导出来

三、模版管理 可以自个添加修改模版,使用的是Razor模版引擎对C#程序员更加友好

四、方案管理,可以创建自已的生成方案,修改方案

五、支持扩展模版属性,支持生成更加丰富的前端代码

六、支持生成解决方案

七、支持生成附加文件,支持文件后缀

八、支持视图

九、支持自定义数据类型

十、支持多种数据库 MYSQL PGSQL SQLITE SQLSERVE  ORCLE  达梦

代码生成器的使用场景

一、提高工作效率

我们都知道一个CRUD其实如果有代码生成器的话只需要几分钟就可以把完整的功能做完,如果用手去写那么可能就要花掉一整天的时间,多出来的这些时间学学技术多好

二、大量重复有共性的功能

使用代码生成器最重要的就是减少大量重复的工作,比如我们有 数据库model 有表单model 有Grid绑定的 model  光写这些MODEL就很头痛,要写各种特性

 

 WebFirst管理和配置功能介绍

 方案管理

  方案是生成的一些配置比如用哪个模版、生成路径、文件名、命名空间等等进行分类和存储 方便下次使用

  内置自带的方案可供学习

 

 

  模版管理

模版是方案的一个属性,创建方案必须选择模版,在WebFirst中我们使用的是Razor模版引擎,功能非常强大,带有智能提示

 

WebFirst专门提供了一个模版编写的小程序让你有智能提示的 点点点,把模版轻松写出来

特色功能

1、在线版CodeFirst

比手动建实体生成表速度更快,只需界面操作选择C#类型(类型可以自定义配置)就能把100个字段的表在几分钟建出来,并且生成表,这种效率手写类根本做不到

2. 快速生成文档

生成的excel文档,比一般的软件生成出来的可能更友好点吧,虽然功能比较一般不过,作为代码生成器+快速开发还是不错的功能

3、支持数据源建类

有的时候可能需要,通过算法或者写个SQL把实体建出来

 

 4、支持生成的时候追附加文件

比如我们要生成项目文件,仓储 ,或者JSON文件等

 

 

5、自定义实体属性配置

这个功能就比较强大了,比如我要给字段设置数据源,控件类型,这样就需要自定义扩展属性进行配置,并且在模版中可以使用这些自定义的特性,每个字段支持多个自定义属性

 

6、自带方案用例

可以先用自带的用方案生成出来看看效果如何,然后改改路径、改改模版就能用到自个的项目中了

 

7、支持备份和还原

将EXE目录下的文件 database文件夹里面的数据库复制进行备份

 

粘贴到生成的项目中

 

我们把EXE目录里面database中的库替换成备份库,这样就实现了还原功能

 

 

下载地址

GITHUB  https://github.com/donet5/WebFirst   实实在在的干货 幸苦开发2个月,感兴趣的可以GITHUB 点个赞

另外我们的另一个开源作品【SqlSugar】 也更新了联表查询语法

1
2
3
4
5
6
var query5 = db.Queryable<Order>()
            .LeftJoin<Custom>((o, cus) => o.CustomId == cus.Id)
            .Where(o => o.Id == 1) 
            .Select((o, cus) => new  { o,cus })
            .ToList(); //现在的语法可以说是相当美观,并且你用where的时候 可以只写一个 o而不是所有都要加上 (o,cus)
//其它框架基本上要  <br>//db.Queryable<Order,Custom>().LeftJoin<Custom><br>//比较下来SqlSugar是不是会更简洁一些呢  

要另外SqlSugar将于10月底支持自动化分表功能5年多的沉淀和设计 最简、最好用、最实用,SqlSugar在2021年和2020可以说不是一框架,

以前用过认为不好用或者没用的也可以关注一下,因为2021开发时间可以等于以前几年开发时间,坚持创新 完美

GITHUB: https://github.com/donet5/SqlSugar   也可以关注一下

MongoDB查询转对象时出错 Element '_id' does not match any field or property of class - 狼窝窝 - 博客园

mikel阅读(49)

来源: MongoDB查询转对象时出错 Element ‘_id’ does not match any field or property of class – 狼窝窝 – 博客园

参考:https://www.cnblogs.com/94cool/p/6230202.html

解决方法:

1、在实体类加:[BsonIgnoreExtraElements]

2、或者定义public ObjectId _id { get; set; }

例子:

[BsonIgnoreExtraElements]

public class BaseData
{
//public ObjectId _id { get; set; }

public string cNo { get; set; }

public string customer { get; set; }

public long batchNo { get; set; }

public DateTime mDate { get; set; }

public string mUser { get; set; }
}

 

顺便保存下数据库帮助类

NuGet需要引用:MongoDB.Driver;

数据库管理工具可以用Robo 3T或者Studio 3T

复制代码
public class MongoDBHelper
    {
        #region 构造函数(初始化集合,子类需重写集合名)
        /// <summary>
        /// 集合
        /// </summary>
        public string _collName { get; set; }
        public MongoDBHelper(string collName)
        {
            this._collName = collName;
        }
        #endregion

        #region 连接配置
        /// <summary>
        /// 链接
        /// </summary>
        private static readonly string conneStr = "mongodb://127.0.0.1:27017";
        /// <summary>
        /// 数据库
        /// </summary>
        private static readonly string dbName = "Testdb";
        #endregion

        #region 单例创建链接
        private static IMongoClient _mongoclient { get; set; }
        private static IMongoClient CreateClient()
        {
            if (_mongoclient == null)
            {
                _mongoclient = new MongoClient(conneStr);
            }
            return _mongoclient;
        }
        #endregion

        #region 获取链接和数据库

        private IMongoClient client = CreateClient();
        public IMongoDatabase _database { get { return _mongoclient.GetDatabase(dbName); } }

        public IMongoDatabase GetDatabase()
        {
            return _database;
        }
        public IMongoCollection<T> GetClient<T>() where T : class, new()
        {
            return _database.GetCollection<T>(_collName);
        }
        #endregion

        #region +InsertMany 批量插入
        /// <summary>
        /// 批量插入
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="t">实体集合</param>
        /// <returns></returns>
        public int InsertMany<T>(List<T> t) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                client.InsertMany(t);
                return 1;
            }
            catch (Exception ex)
            {
                return 0;
            }
        }
        #endregion

        #region +InsertManyAsync 异步批量插入
        /// <summary>
        /// 异步批量插入
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="t">实体集合</param>
        /// <returns></returns>
        public async Task<int> InsertManyAsync<T>(List<T> t) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                await client.InsertManyAsync(t);
                return 1;
            }
            catch
            {
                return 0;
            }
        }
        #endregion
        
        #region +Add 添加一条数据
        /// <summary>
        /// 添加一条数据
        /// </summary>
        /// <param name="t">添加的实体</param>
        /// <param name="host">mongodb连接信息</param>
        /// <returns></returns>
        public int Add<T>(T t) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                client.InsertOne(t);
                return 1;
            }
            catch (Exception ex)
            {
                return 0;
            }
        }
        #endregion

        #region +AddAsync 异步添加一条数据
        /// <summary>
        /// 异步添加一条数据
        /// </summary>
        /// <param name="t">添加的实体</param>
        /// <param name="host">mongodb连接信息</param>
        /// <returns></returns>
        public async Task<int> AddAsync<T>(T t) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                await client.InsertOneAsync(t);
                return 1;
            }
            catch
            {
                return 0;
            }
        }
        #endregion

        #region +Update 修改一条数据
        /// <summary>
        /// 修改一条数据
        /// </summary>
        /// <param name="t">添加的实体</param>
        /// <param name="host">mongodb连接信息</param>
        /// <returns></returns>
        public UpdateResult Update<T>(T t, string id, bool isObjectId = true) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                //修改条件
                FilterDefinition<T> filter;
                if (isObjectId)
                {
                    filter = Builders<T>.Filter.Eq("_id", new ObjectId(id));
                }
                else
                {
                    filter = Builders<T>.Filter.Eq("_id", id);
                }
                //要修改的字段
                var list = new List<UpdateDefinition<T>>();
                foreach (var item in t.GetType().GetProperties())
                {
                    if (item.Name.ToLower() == "id") continue;
                    list.Add(Builders<T>.Update.Set(item.Name, item.GetValue(t)));
                }
                var updatefilter = Builders<T>.Update.Combine(list);
                return client.UpdateOne(filter, updatefilter);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region +UpdateAsync 异步修改一条数据
        /// <summary>
        /// 异步修改一条数据
        /// </summary>
        /// <param name="t">添加的实体</param>
        /// <param name="host">mongodb连接信息</param>
        /// <returns></returns>
        public async Task<UpdateResult> UpdateAsync<T>(T t, string id, bool isObjectId) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                //修改条件
                FilterDefinition<T> filter;
                if (isObjectId)
                {
                    filter = Builders<T>.Filter.Eq("_id", new ObjectId(id));
                }
                else
                {
                    filter = Builders<T>.Filter.Eq("_id", id);
                }
                //要修改的字段
                var list = new List<UpdateDefinition<T>>();
                foreach (var item in t.GetType().GetProperties())
                {
                    if (item.Name.ToLower() == "id") continue;
                    list.Add(Builders<T>.Update.Set(item.Name, item.GetValue(t)));
                }
                var updatefilter = Builders<T>.Update.Combine(list);
                return await client.UpdateOneAsync(filter, updatefilter);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region +UpdateManay 批量修改数据
        /// <summary>
        /// 批量修改数据
        /// </summary>
        /// <param name="dic">要修改的字段</param>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="filter">修改条件</param>
        /// <returns></returns>
        public UpdateResult UpdateManay<T>(Dictionary<string, string> dic, FilterDefinition<T> filter) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                T t = new T();
                //要修改的字段
                var list = new List<UpdateDefinition<T>>();
                foreach (var item in t.GetType().GetProperties())
                {
                    if (!dic.ContainsKey(item.Name)) continue;
                    var value = dic[item.Name];
                    list.Add(Builders<T>.Update.Set(item.Name, value));
                }
                var updatefilter = Builders<T>.Update.Combine(list);
                return client.UpdateMany(filter, updatefilter);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region +UpdateManayAsync 异步批量修改数据
        /// <summary>
        /// 异步批量修改数据
        /// </summary>
        /// <param name="dic">要修改的字段</param>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="filter">修改条件</param>
        /// <returns></returns>
        public async Task<UpdateResult> UpdateManayAsync<T>(Dictionary<string, string> dic, FilterDefinition<T> filter) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                T t = new T();
                //要修改的字段
                var list = new List<UpdateDefinition<T>>();
                foreach (var item in t.GetType().GetProperties())
                {
                    if (!dic.ContainsKey(item.Name)) continue;
                    var value = dic[item.Name];
                    list.Add(Builders<T>.Update.Set(item.Name, value));
                }
                var updatefilter = Builders<T>.Update.Combine(list);
                return await client.UpdateManyAsync(filter, updatefilter);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region Delete 删除一条数据
        /// <summary>
        /// 删除一条数据
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="id">objectId</param>
        /// <returns></returns>
        public DeleteResult Delete<T>(string id, bool isObjectId = true) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                FilterDefinition<T> filter;
                if (isObjectId)
                {
                    filter = Builders<T>.Filter.Eq("_id", new ObjectId(id));
                }
                else
                {
                    filter = Builders<T>.Filter.Eq("_id", id);
                }
                return client.DeleteOne(filter);
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }
        #endregion

        #region DeleteAsync 异步删除一条数据
        /// <summary>
        /// 异步删除一条数据
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="id">objectId</param>
        /// <returns></returns>
        public async Task<DeleteResult> DeleteAsync<T>(string id, bool isObjectId = true) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                //修改条件
                FilterDefinition<T> filter;
                if (isObjectId)
                {
                    filter = Builders<T>.Filter.Eq("_id", new ObjectId(id));
                }
                else
                {
                    filter = Builders<T>.Filter.Eq("_id", id);
                }
                return await client.DeleteOneAsync(filter);
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }
        #endregion

        #region DeleteMany 删除多条数据
        /// <summary>
        /// 删除一条数据
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="filter">删除的条件</param>
        /// <returns></returns>
        public DeleteResult DeleteMany<T>(FilterDefinition<T> filter) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                return client.DeleteMany(filter);
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }
        #endregion

        #region DeleteManyAsync 异步删除多条数据
        /// <summary>
        /// 异步删除多条数据
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="filter">删除的条件</param>
        /// <returns></returns>
        public async Task<DeleteResult> DeleteManyAsync<T>(FilterDefinition<T> filter) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                return await client.DeleteManyAsync(filter);
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }
        #endregion

        #region FindOne 根据id查询一条数据
        /// <summary>
        /// 根据id查询一条数据
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="id">objectid</param>
        /// <param name="field">要查询的字段,不写时查询全部</param>
        /// <returns></returns>
        public T FindOne<T>(string id, bool isObjectId = true, string[] field = null) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                FilterDefinition<T> filter;
                if (isObjectId)
                {
                    filter = Builders<T>.Filter.Eq("_id", new ObjectId(id));  //默认id为objectid类型
                }
                else
                {
                    filter = Builders<T>.Filter.Eq("_id", id);
                }
                //不指定查询字段
                if (field == null || field.Length == 0)
                {
                    return client.Find(filter).FirstOrDefault<T>();
                }

                //制定查询字段
                var fieldList = new List<ProjectionDefinition<T>>();
                for (int i = 0; i < field.Length; i++)
                {
                    fieldList.Add(Builders<T>.Projection.Include(field[i].ToString()));
                }
                var projection = Builders<T>.Projection.Combine(fieldList);
                fieldList?.Clear();
                return client.Find(filter).Project<T>(projection).FirstOrDefault<T>();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region FindOneAsync 异步根据id查询一条数据
        /// <summary>
        /// 异步根据id查询一条数据
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="id">objectid</param>
        /// <returns></returns>
        public async Task<T> FindOneAsync<T>(string id, bool isObjectId = true, string[] field = null) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                FilterDefinition<T> filter;
                if (isObjectId)
                {
                    filter = Builders<T>.Filter.Eq("_id", new ObjectId(id));
                }
                else
                {
                    filter = Builders<T>.Filter.Eq("_id", id);
                }

                //不指定查询字段
                if (field == null || field.Length == 0)
                {
                    return await client.Find(filter).FirstOrDefaultAsync();
                }

                //制定查询字段
                var fieldList = new List<ProjectionDefinition<T>>();
                for (int i = 0; i < field.Length; i++)
                {
                    fieldList.Add(Builders<T>.Projection.Include(field[i].ToString()));
                }
                var projection = Builders<T>.Projection.Combine(fieldList);
                fieldList?.Clear();
                return await client.Find(filter).Project<T>(projection).FirstOrDefaultAsync();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region FindList 查询集合
        /// <summary>
        /// 查询集合
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="filter">查询条件</param>
        /// <param name="field">要查询的字段,不写时查询全部</param>
        /// <param name="sort">要排序的字段</param>
        /// <returns></returns>
        public List<T> FindList<T>(FilterDefinition<T> filter = null, string[] field = null, SortDefinition<T> sort = null) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);

                //不指定查询字段
                if (field == null || field.Length == 0)
                {
                    if (sort == null)
                        return client.Find(filter).ToList();
                    //进行排序
                    return client.Find(filter).Sort(sort).ToList();
                }

                //制定查询字段
                var fieldList = new List<ProjectionDefinition<T>>();
                for (int i = 0; i < field.Length; i++)
                {
                    fieldList.Add(Builders<T>.Projection.Include(field[i].ToString()));
                }
                var projection = Builders<T>.Projection.Combine(fieldList);
                fieldList?.Clear();
                if (sort == null) return client.Find(filter).Project<T>(projection).ToList();
                //排序查询
                return client.Find(filter).Sort(sort).Project<T>(projection).ToList();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region FindListAsync 异步查询集合
        /// <summary>
        /// 异步查询集合
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="filter">查询条件</param>
        /// <param name="field">要查询的字段,不写时查询全部</param>
        /// <param name="sort">要排序的字段</param>
        /// <returns></returns>
        public async Task<List<T>> FindListAsync<T>(FilterDefinition<T> filter, string[] field = null, SortDefinition<T> sort = null) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                //不指定查询字段
                if (field == null || field.Length == 0)
                {
                    if (sort == null) return await client.Find(filter).ToListAsync();
                    return await client.Find(filter).Sort(sort).ToListAsync();
                }

                //制定查询字段
                var fieldList = new List<ProjectionDefinition<T>>();
                for (int i = 0; i < field.Length; i++)
                {
                    fieldList.Add(Builders<T>.Projection.Include(field[i].ToString()));
                }
                var projection = Builders<T>.Projection.Combine(fieldList);
                fieldList?.Clear();
                if (sort == null) return await client.Find(filter).Project<T>(projection).ToListAsync();
                //排序查询
                return await client.Find(filter).Sort(sort).Project<T>(projection).ToListAsync();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region FindListByPage 分页查询集合
        /// <summary>
        /// 分页查询集合
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="filter">查询条件</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">页容量</param>
        /// <param name="count">总条数</param>
        /// <param name="field">要查询的字段,不写时查询全部</param>
        /// <param name="sort">要排序的字段</param>
        /// <returns></returns>
        public List<T> FindListByPage<T>(FilterDefinition<T> filter, int pageIndex, int pageSize, out long count, string[] field = null, SortDefinition<T> sort = null) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                count = client.CountDocuments(filter);
                //不指定查询字段
                if (field == null || field.Length == 0)
                {
                    if (sort == null) return client.Find(filter).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToList();
                    //进行排序
                    return client.Find(filter).Sort(sort).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToList();
                }

                //制定查询字段
                var fieldList = new List<ProjectionDefinition<T>>();
                for (int i = 0; i < field.Length; i++)
                {
                    fieldList.Add(Builders<T>.Projection.Include(field[i].ToString()));
                }
                var projection = Builders<T>.Projection.Combine(fieldList);
                fieldList?.Clear();

                //不排序
                if (sort == null) return client.Find(filter).Project<T>(projection).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToList();

                //排序查询
                return client.Find(filter).Sort(sort).Project<T>(projection).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToList();

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region FindListByPageAsync 异步分页查询集合
        /// <summary>
        /// 异步分页查询集合
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="filter">查询条件</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">页容量</param>
        /// <param name="field">要查询的字段,不写时查询全部</param>
        /// <param name="sort">要排序的字段</param>
        /// <returns></returns>
        public async Task<List<T>> FindListByPageAsync<T>(FilterDefinition<T> filter, int pageIndex, int pageSize, string[] field = null, SortDefinition<T> sort = null) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                //不指定查询字段
                if (field == null || field.Length == 0)
                {
                    if (sort == null) return await client.Find(filter).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
                    //进行排序
                    return await client.Find(filter).Sort(sort).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
                }

                //制定查询字段
                var fieldList = new List<ProjectionDefinition<T>>();
                for (int i = 0; i < field.Length; i++)
                {
                    fieldList.Add(Builders<T>.Projection.Include(field[i].ToString()));
                }
                var projection = Builders<T>.Projection.Combine(fieldList);
                fieldList?.Clear();

                //不排序
                if (sort == null) return await client.Find(filter).Project<T>(projection).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();

                //排序查询
                return await client.Find(filter).Sort(sort).Project<T>(projection).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region Count 根据条件获取总数
        /// <summary>
        /// 根据条件获取总数
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="filter">条件</param>
        /// <returns></returns>
        public long Count<T>(FilterDefinition<T> filter) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                return client.CountDocuments(filter);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region CountAsync 异步根据条件获取总数
        /// <summary>
        /// 异步根据条件获取总数
        /// </summary>
        /// <param name="host">mongodb连接信息</param>
        /// <param name="filter">条件</param>
        /// <returns></returns>
        public async Task<long> CountAsync<T>(FilterDefinition<T> filter) where T : class, new()
        {
            try
            {
                var client = _database.GetCollection<T>(_collName);
                return await client.CountDocumentsAsync(filter);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion
    }
复制代码

 

代码地址:

https://github.com/bill1411/mybase/tree/master/Solution/Mongo