我为在线平台生成DTO对象(Dynamics 365)。最大装配尺寸有非常严格的限制,只有生成的DTO对象占用了该限制的大约80%。我可以对生成的代码进行哪些更改以减少已编译程序集占用的磁盘空间量?
以下是生成的示例类:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Contoso.Xrm.Entities
{
/// <summary>
/// Track changes to records for analysis, record keeping, and compliance.
/// </summary>
[System.Runtime.Serialization.DataContractAttribute()]
[Microsoft.Xrm.Sdk.Client.EntityLogicalNameAttribute("audit")]
[System.CodeDom.Compiler.GeneratedCodeAttribute("CrmSvcUtil", "8.2.1.8676")]
public partial class Audit : Microsoft.Xrm.Sdk.Entity, System.ComponentModel.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged
{
public static class Fields
{
public const string Action = "action";
public const string AttributeMask = "attributemask";
public const string AuditId = "auditid";
public const string Id = "auditid";
public const string CallingUserId = "callinguserid";
public const string CreatedOn = "createdon";
public const string ObjectId = "objectid";
public const string Operation = "operation";
public const string RegardingObjectId = "regardingobjectid";
public const string TransactionId = "transactionid";
public const string UserAdditionalInfo = "useradditionalinfo";
public const string UserId = "userid";
public const string lk_audit_callinguserid = "lk_audit_callinguserid";
public const string lk_audit_userid = "lk_audit_userid";
}
/// <summary>
/// Default Constructor.
/// </summary>
[System.Diagnostics.DebuggerNonUserCode()]
public Audit() :
base(EntityLogicalName)
{
}
public const string EntityLogicalName = "audit";
public const string PrimaryIdAttribute = "auditid";
public const int EntityTypeCode = 4567;
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
public event System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
[System.Diagnostics.DebuggerNonUserCode()]
private void OnPropertyChanged(string propertyName)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
[System.Diagnostics.DebuggerNonUserCode()]
private void OnPropertyChanging(string propertyName)
{
if ((this.PropertyChanging != null))
{
this.PropertyChanging(this, new System.ComponentModel.PropertyChangingEventArgs(propertyName));
}
}
/// <summary>
/// Actions the user can perform that cause a change
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("action")]
public Microsoft.Xrm.Sdk.OptionSetValue Action
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<Microsoft.Xrm.Sdk.OptionSetValue>("action");
}
}
/// <summary>
/// Contains a CSV of the ColumnNumber metadata property of attributes
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("attributemask")]
public string AttributeMask
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<string>("attributemask");
}
}
/// <summary>
/// Unique identifier of the auditing instance
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("auditid")]
public System.Nullable<System.Guid> AuditId
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<System.Nullable<System.Guid>>("auditid");
}
}
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("auditid")]
public override System.Guid Id
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return base.Id;
}
[System.Diagnostics.DebuggerNonUserCode()]
set
{
base.Id = value;
}
}
/// <summary>
/// Unique identifier of the calling user in case of an impersonated call
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("callinguserid")]
public Microsoft.Xrm.Sdk.EntityReference CallingUserId
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<Microsoft.Xrm.Sdk.EntityReference>("callinguserid");
}
}
/// <summary>
/// Date and time when the audit record was created.
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("createdon")]
public System.Nullable<System.DateTime> CreatedOn
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<System.Nullable<System.DateTime>>("createdon");
}
}
/// <summary>
/// Unique identifier of the record that is being audited
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("objectid")]
public Microsoft.Xrm.Sdk.EntityReference ObjectId
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<Microsoft.Xrm.Sdk.EntityReference>("objectid");
}
}
/// <summary>
/// The action that causes the audit--it will be create, delete, or update
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("operation")]
public Microsoft.Xrm.Sdk.OptionSetValue Operation
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<Microsoft.Xrm.Sdk.OptionSetValue>("operation");
}
}
/// <summary>
/// Unique identifier of the object with which the record is associated.
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("regardingobjectid")]
public Microsoft.Xrm.Sdk.EntityReference RegardingObjectId
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<Microsoft.Xrm.Sdk.EntityReference>("regardingobjectid");
}
[System.Diagnostics.DebuggerNonUserCode()]
set
{
this.OnPropertyChanging("RegardingObjectId");
this.SetAttributeValue("regardingobjectid", value);
this.OnPropertyChanged("RegardingObjectId");
}
}
/// <summary>
/// Unique identifier for multiple changes that are part of a single operation; this field contains the same GUID for all the audit rows generated in a single transaction
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("transactionid")]
public System.Nullable<System.Guid> TransactionId
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<System.Nullable<System.Guid>>("transactionid");
}
}
/// <summary>
/// Additional information associated to the user who caused the change.
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("useradditionalinfo")]
public string UserAdditionalInfo
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<string>("useradditionalinfo");
}
[System.Diagnostics.DebuggerNonUserCode()]
set
{
this.OnPropertyChanging("UserAdditionalInfo");
this.SetAttributeValue("useradditionalinfo", value);
this.OnPropertyChanged("UserAdditionalInfo");
}
}
/// <summary>
/// Unique identifier of the user who caused a change
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("userid")]
public Microsoft.Xrm.Sdk.EntityReference UserId
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<Microsoft.Xrm.Sdk.EntityReference>("userid");
}
}
/// <summary>
/// 1:N userentityinstancedata_audit
/// </summary>
[Microsoft.Xrm.Sdk.RelationshipSchemaNameAttribute("userentityinstancedata_audit")]
public System.Collections.Generic.IEnumerable<Contoso.Xrm.Entities.UserEntityInstanceData> userentityinstancedata_audit
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetRelatedEntities<Contoso.Xrm.Entities.UserEntityInstanceData>("userentityinstancedata_audit", null);
}
[System.Diagnostics.DebuggerNonUserCode()]
set
{
this.OnPropertyChanging("userentityinstancedata_audit");
this.SetRelatedEntities<Contoso.Xrm.Entities.UserEntityInstanceData>("userentityinstancedata_audit", null, value);
this.OnPropertyChanged("userentityinstancedata_audit");
}
}
/// <summary>
/// N:1 lk_audit_callinguserid
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("callinguserid")]
[Microsoft.Xrm.Sdk.RelationshipSchemaNameAttribute("lk_audit_callinguserid")]
public Contoso.Xrm.Entities.SystemUser lk_audit_callinguserid
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetRelatedEntity<Contoso.Xrm.Entities.SystemUser>("lk_audit_callinguserid", null);
}
}
/// <summary>
/// N:1 lk_audit_userid
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("userid")]
[Microsoft.Xrm.Sdk.RelationshipSchemaNameAttribute("lk_audit_userid")]
public Contoso.Xrm.Entities.SystemUser lk_audit_userid
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetRelatedEntity<Contoso.Xrm.Entities.SystemUser>("lk_audit_userid", null);
}
}
/// <summary>
/// Constructor for populating via LINQ queries given a LINQ anonymous type
/// <param name="anonymousType">LINQ anonymous type.</param>
/// </summary>
[System.Diagnostics.DebuggerNonUserCode()]
public Audit(object anonymousType) :
this()
{
foreach (var p in anonymousType.GetType().GetProperties())
{
var value = p.GetValue(anonymousType, null);
var name = p.Name.ToLower();
if (name.EndsWith("enum") && value.GetType().BaseType == typeof(System.Enum))
{
value = new Microsoft.Xrm.Sdk.OptionSetValue((int) value);
name = name.Remove(name.Length - "enum".Length);
}
switch (name)
{
case "id":
base.Id = (System.Guid)value;
Attributes["auditid"] = base.Id;
break;
case "auditid":
var id = (System.Nullable<System.Guid>) value;
if(id == null){ continue; }
base.Id = id.Value;
Attributes[name] = base.Id;
break;
case "formattedvalues":
// Add Support for FormattedValues
FormattedValues.AddRange((Microsoft.Xrm.Sdk.FormattedValueCollection)value);
break;
default:
Attributes[name] = value;
break;
}
}
}
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("action")]
public virtual Audit_Action? ActionEnum
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return ((Audit_Action?)(EntityOptionSetEnum.GetEnum(this, "action")));
}
}
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("operation")]
public virtual Audit_Operation? OperationEnum
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return ((Audit_Operation?)(EntityOptionSetEnum.GetEnum(this, "operation")));
}
}
}
}
答案 0 :(得分:4)
查看您的DLaB.Xrm.Entities.dll程序集(4.5Mb),它包含大约700个类,其中许多类具有大量属性。 该程序集包含大约1/3的字符串(如果您使用二进制编辑器查看它)。
没有什么可以优化的。但是,这里有一些想法:
public partial class Account : Microsoft.Xrm.Sdk.Entity, System.ComponentModel.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged
{
...
public Microsoft.Xrm.Sdk.OptionSetValue AccountRatingCode
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<Microsoft.Xrm.Sdk.OptionSetValue>("accountratingcode");
}
[System.Diagnostics.DebuggerNonUserCode()]
set
{
this.OnPropertyChanging("AccountRatingCode");
this.SetAttributeValue("accountratingcode", value);
this.OnPropertyChanged("AccountRatingCode");
}
}
...
}
我试图摆脱OnPropxxx调用,如下所示:
public partial class Account : MyBaseEntity
{
...
public Microsoft.Xrm.Sdk.OptionSetValue AccountRatingCode
{
get
{
return this.GetAttributeValue<Microsoft.Xrm.Sdk.OptionSetValue>("accountratingcode");
}
set
{
this.SetAttributeValue("accountratingcode", value);
}
}
...
}
此外,所有实体类的构造函数似乎都遵循标准模式。也许可以看一下。
无论如何,我已经使用Roslyn CSharpSyntaxRewriter完成了一些测试。并且我可以移除所有可以减少20-25%的装配尺寸。
无论你决定减少什么,编写一个Roslyn重写器似乎都是这个的好工具,所以这是我的示例代码:
class Program
{
static void Main(string[] args)
{
Do().Wait();
}
static async Task Do()
{
var ws = MSBuildWorkspace.Create();
var project = await ws.OpenProjectAsync(@"..\..\DLaB.Xrm.Entities\DLaB.Xrm.Entities.csproj"); // initial DLaB.Xrm.Entities project from your github
Task.WaitAll(new List<Task>(project.Documents.Select(Rewrite)).ToArray());
}
static async Task Rewrite(Document doc)
{
var tree = await doc.GetSyntaxTreeAsync();
var root = await tree.GetRootAsync();
var optimizer = new EntitySizeOptimizer();
var result = optimizer.Visit(root);
string dir = @"..\..\DLaB.Xrm.Entities2"; // a copy of DLaB.Xrm.Entities project to compare size
string path = Path.Combine(dir, string.Join(@"\", doc.Folders), doc.Name);
File.WriteAllText(path, result.ToFullString());
Console.WriteLine(path);
}
}
class EntitySizeOptimizer : CSharpSyntaxRewriter
{
// try the MyBaseEntity approach (note I just removed the OnPropXXX calls for testing, I've not created the whole base class, it's more work).
static SyntaxNode Nop = SyntaxFactory.ParseExpression(""); // I'm sure there's something smarter than this...
public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
{
string text = node.ToFullString().Trim();
if (text.StartsWith("this.OnPropertyChang")) // bit of a hack...
return Nop;
return base.VisitInvocationExpression(node);
}
// remove Fields
public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node)
{
if (node.Identifier.ValueText == "Fields")
return null;
return base.VisitStructDeclaration(node);
}
// remove useless attributes
public override SyntaxNode VisitAttributeList(AttributeListSyntax node)
{
var atts = new SeparatedSyntaxList<AttributeSyntax>().AddRange(FilterAttributes(node.Attributes));
if (atts.Count == 0) // nothing left, remove this node
return null;
if (atts.Count == node.Attributes.Count) // no change, don't change the tree
return base.VisitAttributeList(node);
return node.WithAttributes(atts); // rewrite
}
private static IEnumerable<AttributeSyntax> FilterAttributes(IEnumerable<AttributeSyntax> atts)
{
foreach (var att in atts)
{
if (IsSameAttribute(att, typeof(EnumMemberAttribute).FullName))
continue;
if (IsSameAttribute(att, typeof(DebuggerNonUserCodeAttribute).FullName))
continue;
if (IsSameAttribute(att, typeof(GeneratedCodeAttribute).FullName))
continue;
yield return att;
}
}
private static bool IsSameAttribute(AttributeSyntax node, string text)
{
return node.Name is QualifiedNameSyntax qn && IsSameAttribute(text, qn.ToFullString());
}
// note: we could (should?) use Roslyn Semantic Model, but this is faster...
private static bool IsSameAttribute(string att1, string att2)
{
if (att1 == att2)
return true;
string StripAttribute(string att)
{
const string token = "Attribute";
return att.EndsWith(token) ? att.Substring(0, att.Length - token.Length) : att;
}
return StripAttribute(att1) == StripAttribute(att2);
}
}
答案 1 :(得分:0)
在CodeGolf上有一个线程,其中包含缩短C#代码的所有技巧:https://codegolf.stackexchange.com/questions/173/tips-for-code-golfing-in-c
<强>更新强>
较少的字符是否转换为光盘上较少的编译字节?
我实际上并不认为它可以,如果是这样不是很多。
不幸的是,我发现了一个糟糕的例子,因为小的实际上是更大的,从来没有证明你可以通过Code 打高尔夫球来减少占地面积。 虽然没有Simon的方法那么有效。
<强>小:强>
class Program
{
static void Main(string[] args)
{
for (int i = 0;i< 5;i++)
{
Console.Write(i+"");
}
}
}
=大小:5.00 KB(5,120字节)
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 29 (0x1d)
.maxstack 2
.entrypoint
.locals init (
[0] int32 i
)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_0018
// loop start (head: IL_0018)
IL_0004: ldloc.0
IL_0005: box [mscorlib]System.Int32
IL_000a: call string [mscorlib]System.String::Concat(object)
IL_000f: call void [mscorlib]System.Console::Write(string)
IL_0014: ldloc.0
IL_0015: ldc.i4.1
IL_0016: add
IL_0017: stloc.0
IL_0018: ldloc.0
IL_0019: ldc.i4.5
IL_001a: blt.s IL_0004
// end loop
IL_001c: ret
} // end of method Program::Main
<强>大强>
class Program
{
static void Main(string[] args)
{
for (int i = 0;i< 5;i++)
{
Console.Write(i.ToString());
}
}
}
=大小:4.50 KB(4,608字节)
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 25 (0x19)
.maxstack 2
.entrypoint
.locals init (
[0] int32
)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_0014
// loop start (head: IL_0014)
IL_0004: ldloca.s 0
IL_0006: call instance string [mscorlib]System.Int32::ToString()
IL_000b: call void [mscorlib]System.Console::Write(string)
IL_0010: ldloc.0
IL_0011: ldc.i4.1
IL_0012: add
IL_0013: stloc.0
IL_0014: ldloc.0
IL_0015: ldc.i4.5
IL_0016: blt.s IL_0004
// end loop
IL_0018: ret
} // end of method Program::Main
至少我们可以看到这种压缩方法没什么价值。