基于Enterprise Library PIAB的AOP框架已经在公司项目开发中得到广泛的使用,但是最近同事维护一个老的项目,使用到了Enterprise Library 2,所以PIAB是在Enterprise Library 3.0中推出的,所以不同直接使用。为了解决这个问题,我写了一个通过方法劫持(Method Interception)的原理,写了一个简易版的AOP框架。(如果对PIAB不是很了解的读者,可以参阅我的文章MS Enterprise Library Policy Injection Application Block 深入解析[总结篇])。 Souce Code下载:http://files.cnblogs.com/artech/Artech.SimpleAopFramework.zip
一、如何使用?
编程方式和PIAB基本上是一样的,根据具体的需求创建相应的CallHandler,通过Custom Attribute的形式将CallHandler应用到类型或者方法上面。下面就是一个简单例子。

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->usingSystem;
usingSystem.Collections.Generic;
usingSystem.Data.Common;
usingSystem.Data.SqlClient;
usingArtech.SimpleAopFramework.Demos;
usingSystem.Configuration;
namespaceArtech.SimpleAopFramework
{
classProgram
{
staticvoidMain(string[]args)
{
stringuserID=Guid.NewGuid().ToString();
InstanceBuilder.Create<UserManager,IUserManager>().CreateDuplicateUsers(userID,Guid.NewGuid().ToString());
Console.WriteLine("IstheuserwhoseIDis\"{0}\"hasbeensuccessfullycreated!{1}",userID,UserUtility.UserExists(userID)?"Yes":"No");
}
}
publicclassUserManager:IUserManager
{
[ExceptionCallHandler(Ordinal=1,MessageTemplate="Encountererror:\nMessage:{Message}")]
[TransactionScopeCallHandler(Ordinal=2)]
publicvoidCreateDuplicateUsers(stringuserID,stringuserName)
{
UserUtility.CreateUser(userID,userName);
UserUtility.CreateUser(userID,userName);
}
}
publicinterfaceIUserManager
{
voidCreateDuplicateUsers(stringuserID,stringuserName);
}
}
在上面例子中,我创建了两个CallHandler:TransactionScopeCallHandler和ExceptionCallHandler,用于进行事务和异常的处理。也就是说,我们不需要手工地进行事务的Open、Commit和Rollback的操作,也不需要通过try/catch block进行手工的异常处理。为了验证正确性,我模拟了这样的场景:数据库中有一个用户表(Users)用于存储用户帐户,每个帐户具有唯一ID,现在我通过UserManager的CreateDuplicateUsers方法插入两个具有相同ID的记录,毫无疑问,如果没有事务的处理,第一次用户添加将会成功,第二次将会失败。反之如果我们添加的TransactionScopeCallHandler能够起作用,两次操作将在同一个事务中进行,重复的记录添加将会导致怎过事务的回退。
在ExceptionCallHandler中,会对抛出的SqlException进行处理,在这我们仅仅是打印出异常相关的信息。至于具有要输出那些信息,可以通过ExceptionCallHandlerAttribute的MessageTemplate 属性定义一个输出的模板。
运行程序,我们会得到这样的结果,充分证明了事务的存在,错误信息也按照我们希望的模板进行输出。
二、设计概要
同PIAB的实现原理一样,我通过自定义RealProxy实现对CallHandler的执性,从而达到方法调用劫持的目的(底层具体的实现,可以参阅我的文章Policy Injection Application Block 设计和实现原理)。下面的UML列出了整个框架设计的所有类型。
- ICallHandler:所有CallHandler必须实现的接口。
- CallHandlerBase:实现了ICallHandler的一个抽象类,是自定义CallHandler的基类。
- HandlerAttribute:所有的CallHandler通过相应的HandlerAttribute被应用到所需的目标对象上。HandlerAttribute是一个继承自Attribute的抽象类,是自定义HandlerAttribute的基类。
- CallHandlerPipeline:由于同一个目标方法上面可以同时应用多个CallHandler,在运行时,他们被串成一个有序的管道,依次执行。
- InterceptingRealProxy<T>:继承自RealProxy,CallHandlerPipeline最终在Invoke方法中执行,从而实现了“方法调用劫持”。
- InvocationContext:表示当前方法执行的上下文,Request和Reply成员表示方法的调用和返回消息。
- InstanceBuidler:由于我们需根据InterceptingRealProxy<T>对象创建TransparentProxy,并通过TransparentProxy进行方法的调用,CallHandler才能在RealProxy中被执行。InstanceBuilder用于方便的创建TransparentProxy对象。
三、具体实现
现在我们来详细分析实现的细节。下来看看表示方法调用上下文的InvocationContext的定义。
1、InvocationContext

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->usingSystem.Collections.Generic;
usingSystem.Runtime.Remoting.Messaging;
namespaceArtech.SimpleAopFramework
{
///<summary>
///InvocationContextrepresentsthecontextofamethodinvocation.
///</summary>
publicclassInvocationContext
{
///<summary>
///A<seecref="IMethodCallMessage"/>objectrepresentsthemethodcall.
///</summary>
publicIMethodCallMessageRequest
{get;set;}
///<summary>
///A<seecref="ReturnMessage"/>objectrepresentsthereturnofthemethodcall.
///</summary>
publicReturnMessageReply
{get;set;}
///<summary>
///A<seecref="IDictionary<object,object>"/>objectusedtosetextracontextualinformation.
///</summary>
publicIDictionary<object,object>Properties
{get;set;}
}
}
Request和Reply本质上都是一个System.Runtime.Remoting.Messaging.IMessage对象。Request是IMethodCallMessage 对象,表示方法调用的消息,Reply则是ReturnMessage对象,具有可以包含具体的返回值,也可以包含抛出的异常。Properties可以供我们自由地设置一些自定义的上下文。
2、ICallHandler、CallHandlerBase和HandlerAttribute
ICallHandler包含四个成员,PreInvoke和PostInvoke在执行目标方法前后被先后调用,自定义CallHandler可以根据自己的具体需求实现这个两个方法。PreInvoke返回值可以通过PostInvoke的correlationState获得。Ordinal表明CallHandler在CallHandler管道的位置,他决定了应用于同一个目标方法上的多个CallHandler的执行顺序。ReturnIfError表示CallHandler在抛出异常时是否直接退出。

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->namespaceArtech.SimpleAopFramework
{
///<summary>
///Allofthecustomcallhandlermustdireclyorindirectlyimpplementthisinterface.
///</summary>
publicinterfaceICallHandler
{
///<summary>
///Thismethodwillbeinjectedintotheinvocationstackpriortoinvoketargetmethod.
///</summary>
///<paramname="context">A<seecref="InvocationContext"/>objectindicatingthecurrentmethodinvocationcontext.</param>
///<returns>TheuserstatewhichcanbepickedupinthePostInvokemethod.</returns>
objectPreInvoke(InvocationContextcontext);
///<summary>
///Thismethodwillbeinjectedintotheinvocationstackaftertargetmethodisinvoked.
///</summary>
///<paramname="context">A<seecref="InvocationContext"/>objectindicatingthecurrentmethodinvocationcontext.</param>
///<paramname="correlationState">TheuserstatereturnedfromthePreinvokemethod.</param>
voidPostInvoke(InvocationContextcontext,objectcorrelationState);
///<summary>
///A<seecref="int"/>valueindicatingthepositionwherethecallhandlerisplacedinthepipeline.
///</summary>
intOrdinal
{get;set;}
///<summary>
///A<seecref="bool"/>valueindicatingwhethertodirectlyreturnifencounteringerror.
///</summary>
boolReturnIfError
{get;set;}
}
}
CallHandler的抽象基类CallHandlerBase仅仅是对ICallHandler的简单实现。

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->namespaceArtech.SimpleAopFramework
{
///<summary>
///Thebaseclassofallcustomcallhandler.
///</summary>
publicabstractclassCallHandlerBase:ICallHandler
{
#regionICallHandlerMembers
///<summary>
///Themethodwillbeinjectedintotheinvocationstackpriortoexecutethetargetmethod.
///</summary>
///<paramname="context">A<seecref="InvocationContext"/>objectpresentingthecurrentmethodinvocationcontext.</param>
///<returns>AstateobjectwhichcanbepickupbyPostInvokemethod.</returns>
publicabstractobjectPreInvoke(InvocationContextcontext);
///<summary>e
///Themethodwillbeinjectedintotheinvicationstackafterthetargetmethodisinvoked.
///</summary>
///<paramname="context">A<seecref="InvocationContext"/>objectpresentingthecurrentmethodinvocationcontext.</param>
///<paramname="correlationState">AsateobjectreturnedfromPreInvokemethod.</param>
publicabstractvoidPostInvoke(InvocationContextcontext,objectcorrelationState);
///<summary>
///A<seecref="int"/>valueindicatingthepositionwherethecallhandlerisplacedinthepipeline.
///</summary>
publicintOrdinal
{get;set;}
///<summary>
///A<seecref="bool"/>valueindicatingwhethertodirectlyreturnifencounteringerror.
///</summary>
publicboolReturnIfError
{get;set;}
#endregion
}
}
HandlerAttribute中定义了CreateCallHandler方法创建相应的CallHandler对象,Ordinal和ReturnIfError同上。

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->usingSystem;
namespaceArtech.SimpleAopFramework
{
///<summary>
///Thebaseclassofcustomcallhandlerattribute.
///</summary>
publicabstractclassHandlerAttribute:Attribute
{
///<summary>
///Allofthesubclassmustimplementthismethodtocreate<seecref="ICallHandler"/>object.
///</summary>
///<returns>Thecreated<seecref="ICallHandler"/>object</returns>
publicabstractICallHandlerCreateCallHandler();
///<summary>
///A<seecref="int"/>valueindicatingthepositionwherethecallhandlerisplacedinthepipeline.
///</summary>
publicintOrdinal
{get;set;}
///<summary>
///A<seecref="bool"/>valueindicatingwhethertodirectlyreturnifencounteringanerror.
///</summary>
publicboolReturnIfError
{get;set;}
}
}
3、CallHandlerPipeline
CallHandlerPipeline是CallHandler的有序集合,我们通过一个IList<ICallHandler> 对象和代码最终目标对象的创建CallHandlerPipeline。CallHandlerPipeline的核心方法是Invoke。在Invoke方法中按照CallHandler在管道中的次序先执行PreInvoke方法,然后通过反射执行目标对象的相应方法,最后逐个执行CallHandler的PostInvoke方法。

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Runtime.Remoting.Messaging;
namespaceArtech.SimpleAopFramework
{
publicclassCallHandlerPipeline
{
privateobject_target;
privateIList<ICallHandler>_callHandlers;
publicCallHandlerPipeline(objecttarget)
:this(newList<ICallHandler>(),target)
{}
publicCallHandlerPipeline(IList<ICallHandler>callHandlers,objecttarget)
{
if(target==null)
{
thrownewArgumentNullException("target");
}
if(callHandlers==null)
{
thrownewArgumentNullException("callHandlers");
}
this._target=target;
this._callHandlers=callHandlers;
}
///<summary>
///Invokethecallhandlerandthetargetinstaneonebyone.
///</summary>
///<paramname="context"></param>
publicvoidInvoke(InvocationContextcontext)
{
Stack<object>correlationStates=newStack<object>();
Stack<ICallHandler>callHandlerStack=newStack<ICallHandler>();
//Preinvoke.
foreach(ICallHandlercallHandlerinthis._callHandlers)
{
correlationStates.Push(callHandler.PreInvoke(context));
if(context.Reply!=null&&context.Reply.Exception!=null&&callHandler.ReturnIfError)
{
context.Reply=newReturnMessage(context.Reply.Exception,context.Request);
return;
}
callHandlerStack.Push(callHandler);
}
//InvokeTargetObject.
object[]copiedArgs=Array.CreateInstance(typeof(object),context.Request.Args.Length)asobject[];
context.Request.Args.CopyTo(copiedArgs,0);
try
{
objectreturnValue=context.Request.MethodBase.Invoke(this._target,copiedArgs);
context.Reply=newReturnMessage(returnValue,copiedArgs,copiedArgs.Length,context.Request.LogicalCallContext,context.Request);
}
catch(Exceptionex)
{
context.Reply=newReturnMessage(ex,context.Request);
}
//PostInvoke.
while(callHandlerStack.Count>0)
{
ICallHandlercallHandler=callHandlerStack.Pop();
objectcorrelationState=correlationStates.Pop();
callHandler.PostInvoke(context,correlationState);
}
}
///<summary>
///SortallofthecallhandlerbasedonOrderinal.
///</summary>
publicvoidSort()
{
ICallHandler[]callHandlers=this._callHandlers.ToArray<ICallHandler>();
ICallHandlerswaper=null;
for(inti=0;i<callHandlers.Length-1;i++)
{
for(intj=i+1;j<callHandlers.Length;j++)
{
if(callHandlers[i].Ordinal>callHandlers[j].Ordinal)
{
swaper=callHandlers[i];
callHandlers[i]=callHandlers[j];
callHandlers[j]=swaper;
}
}
}
this._callHandlers=callHandlers.ToList<ICallHandler>();
}
///<summary>
///Comineanew<seecref="CallHandlerPipeline"/>object.
///</summary>
///<paramname="pipeline">The<seecref="CallHandlerPipeline"/>objecttocombine.</param>
publicvoidCombine(CallHandlerPipelinepipeline)
{
if(pipeline==null)
{
thrownewArgumentNullException("pipeline");
}
foreach(ICallHandlercallHandlerinpipeline._callHandlers)
{
this.Add(callHandler);
}
}
///<summary>
///Combineanew<seecref="IList<ICallHandler>"/>object.
///</summary>
///<paramname="callHandlers">The<seecref="IList<ICallHandler>"/>objecttocombine.</param>
publicvoidCombine(IList<ICallHandler>callHandlers)
{
if(callHandlers==null)
{
thrownewArgumentNullException("callHandlers");
}
foreach(ICallHandlercallHandlerincallHandlers)
{
this.Add(callHandler);
}
}
///<summary>
///Addanew<seecref="ICallHandler"/>objecttothecallhandlerpipeline.
///</summary>
///<paramname="callHandler">The<seecref="ICallHandler"/>objecttoadd.</param>
///<returns>The<seecref="ICallHandler"/>objecttoadd.</returns>
publicICallHandlerAdd(ICallHandlercallHandler)
{
if(callHandler==null)
{
thrownewArgumentNullException("callHandler");
}
this._callHandlers.Add(callHandler);
returncallHandler;
}
}
}
4、InterceptionRealProxy<T>
InterceptingRealProxy<T>是现在AOP的关键所在,我们通过一个IDictionary<MemberInfo, CallHandlerPipeline>和目标对象创建InterceptingRealProxy对象。在Invoke方法中,根据方法表示方法调用的IMethodCallMessage对象的MethodBase为key,从CallHandlerPipeline字典中获得基于当前方法的CallHandlerPipeline,并调用它的Invoke方法,InvocationContext的Reply即为最终的返回。

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->usingSystem;
usingSystem.Collections.Generic;
usingSystem.Runtime.Remoting.Proxies;
usingSystem.Runtime.Remoting.Messaging;
usingSystem.Reflection;
namespaceArtech.SimpleAopFramework
{
///<summary>
///TheInterceptingRealProxyisacustom<seecref="RealProxy"/>usedtoperformmethodinterception.
///</summary>
///<typeparamname="T">Thetyperepresentingthedeclarationtypeofthetransparentproxy.</typeparam>
publicclassInterceptingRealProxy<T>:RealProxy
{
privateIDictionary<MemberInfo,CallHandlerPipeline>_callHandlerPipelines;
publicInterceptingRealProxy(objecttarget,IDictionary<MemberInfo,CallHandlerPipeline>callHandlerPipelines)
:base(typeof(T))
{
if(callHandlerPipelines==null)
{
thrownewArgumentNullException("callHandlerPipelines");
}
this._callHandlerPipelines=callHandlerPipelines;
}
///<summary>
///Themethodinvocaitonisinterceptedandalloftherelatedcallhandlersisinvoked.
///</summary>
///<paramname="msg">A<seecref="IMessage"/>representingmethodcall.</param>
///<returns>A<seecref="IMessage"/>representigthereturnofmethodinvocation.</returns>
publicoverrideIMessageInvoke(IMessagemsg)
{
InvocationContextcontext=newInvocationContext();
context.Request=(IMethodCallMessage)msg;
this._callHandlerPipelines[context.Request.MethodBase].Invoke(context);
returncontext.Reply;
}
}
}
5、InstanceBuidler
同PIAB通过PolicyInjection.Create()/Wrap()创建Transparent Proxy类型,InstanceBuidler也充当这样的工厂功能。InstanceBuidler的实现原理就是:通过反射获得目标类型上所有的HandlerAttribute,通过调用HandlerAttribute的CreateCallHandler创建相应的CallHandler。对于每个具体的方法,将应用在其类和方法上的所有的CallHandler组合成CallHandlerPipeline,然后以MemberInfo对象为Key将所有基于某个方法的CallHandlerPipeline构成一个CallHandlerPipeline字典。该字典,连同通过反射创建的目标对象,创建InterceptingRealProxy<T>对象。最后返回InterceptingRealProxy<T>对象的TransparentProxy对象。
相关推荐
MTPA数值求解:双法探究,MTPA数值求解详解:两种方法的比较与应用探索,MTPA数值求解两种方法 ,MTPA数值求解; 方法一; 方法二;,MTPA数值求解的两种高效方法
# 踏入C语言的奇妙编程世界 在编程的广阔宇宙中,C语言宛如一颗璀璨恒星,以其独特魅力与强大功能,始终占据着不可替代的地位。无论你是编程小白,还是有一定基础想进一步提升的开发者,C语言都值得深入探索。 C语言的高效性与可移植性令人瞩目。它能直接操控硬件,执行速度快,是系统软件、嵌入式开发的首选。同时,代码可在不同操作系统和硬件平台间轻松移植,极大节省开发成本。 学习C语言,能让你深入理解计算机底层原理,培养逻辑思维和问题解决能力。掌握C语言后,再学习其他编程语言也会事半功倍。 现在,让我们一起开启C语言学习之旅。这里有丰富教程、实用案例、详细代码解析,助你逐步掌握C语言核心知识和编程技巧。别再犹豫,加入我们,在C语言的海洋中尽情遨游,挖掘无限可能,为未来的编程之路打下坚实基础!
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
'Function 生成视频缩略图(ByVal 视频文件 As String, ByVal 保存缩略图的文件路径 As String, Optional ByVal jpg图像品质 As Long = 80, _ ' Optional ByVal 缩略图宽度 As Long = 500, Optional ByVal 缩略图高度 As Long = 500 _ ' , Optional 返回图像实际宽度 As Long, Optional 返回图像实际高度 As Long) As Boolean Public Function SaveImageAs(LoadImgFile As String, ByVal SaveAsImgFile As String, _ Optional ByVal JpgQuality As Long = 80, Optional hPal As Long, Optional Resolution As Single) As Boolean
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
bkall_answers(2).json
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
近日,一份由清华大学团队发布《DeepSeek:从入门到精通》的AI学习教程冲上了热搜,它是由清华大学新闻与传播学院新媒体研究中心元宇宙文化实验室的余梦珑博士后及其团队倾力打造,从三个方面深入剖析了DeepSeek,DeepSeek是什么?有什么用?怎么使用? 详细论述了其应用场景与使用方法,并讲解了如何通过设计精妙的提示语来提升AI的使用效率,以及丰富的实例干货。 全部104页,完整版资料已经帮大家整理好了,免费领取 资料链接: https://pan.quark.cn/s/be3b500c539c
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
冰点下载器珍藏版.zip
Wallpaper Engine 是一款广受欢迎的动态壁纸软件,允许用户将各种动态、交互式壁纸应用到桌面上。其丰富的创意工坊内容让用户可以轻松下载和分享个性化的壁纸。而“一键提取”功能则是 Wallpaper Engine 中一个非常实用的工具,能够帮助用户快速提取和保存壁纸资源,方便后续使用或分享。
科研人员
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
13考试真题最近的t66.txt
对外承包项目借款合同2[示范文本].doc
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
借助java编程语言和MySQL数据库等实现系统的全部功能,接下来对系统进行测试,测试系统是否有漏洞和测试用户权限来完善系统,最终系统完成达到相关标准。 本系统主要包括管理员和用户两个角色;主要包括首页、个人中心、用户管理、商品分类管理、商品信息管理、促销产品管理、系统管理、订单管理等功能的管理系统。 系统权限按管理员和用户这两类涉及用户划分。 (1)管理员功能需求 管理员登陆后,主要包括首页、个人中心、用户管理、商品分类管理、商品信息管理、促销产品管理、系统管理、订单管理等功能。 2)用户功能需求 用户登陆后进入小程序首页,可以实现首页、商品信息、促销产品、购物车、我的等,在我的页面可以对个人中心、我的收藏管理、用户充值、购物车、我的订单等功能进行详细操作。