private static T getPrivate<T>(this object obj, string privateField)
{
return (T)obj?.GetType()
.GetField(privateField, BindingFlags.Instance | BindingFlags.NonPublic)
?.GetValue(obj);
}
public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
{
var enumerator = query.Provider.Execute<IEnumerable<TEntity>>(query.Expression).GetEnumerator();
var relationalQueryContext = enumerator.getPrivate<RelationalQueryContext>("_relationalQueryContext");
var relationalCommandCache = enumerator.getPrivate<RelationalCommandCache>("_relationalCommandCache");
#pragma warning disable EF1001 // Internal EF Core API usage.
IRelationalCommand command = relationalCommandCache.GetRelationalCommand(relationalQueryContext.ParameterValues);
#pragma warning restore EF1001 // Internal EF Core API usage.
return command.CommandText;
}
2020-02-13 10:35
EF Core 3.1 取得 IQueryable 的 SQL
2019-07-22 16:49
C# COM 元件使用 MTA
先前有用到一個通訊用的 COM 元件,因為連線不穩的時候會影響到 Main Thread 造成這個 WinForm UI 卡住,連帶所有 Main Thread 下的其他 Thread 都卡住,最先找到的方法是在 Program Main 上改用 MTAThreadAttribute,的確是可以解決卡住的問題。
但 WPF 就不可以用 MTAThreadAttribute,因為 WPF 必須執行在 STAThread 的環境上,又開始苦惱這個問題了,問題應該還是有解套的辦法的只是知識不足,最後在 WIKI 中看到重要的知識。
WIKI 元件物件模型
所以只要用其他 Thread 去建立 COM 元件就不會影響到 Main Thread 了,簡單的解決問題,果然是知識不足。
但 WPF 就不可以用 MTAThreadAttribute,因為 WPF 必須執行在 STAThread 的環境上,又開始苦惱這個問題了,問題應該還是有解套的辦法的只是知識不足,最後在 WIKI 中看到重要的知識。
WIKI 元件物件模型
一個COM物件只能存在於一個套間。COM物件一經建立就確定所屬套間,並且直到銷毀它一直存在於這個套間。
所以只要用其他 Thread 去建立 COM 元件就不會影響到 Main Thread 了,簡單的解決問題,果然是知識不足。
ActEasyIF actConnection;
var waiter = new AutoResetEvent(false);
new Thread(() =>
{
/* 建構 COM 元件 */
_actConnection = new ActEasyIF();
waiter.Set();
}).Start();
waiter.WaitOne(500); /* 等待建立結束 */
2019-07-22 15:36
C# struct 轉換到 byte array
StructLayout: https://docs.microsoft.com/zh-tw/dotnet/api/system.runtime.interopservices.layoutkind?view=netframework-4.8
Pack: 資料欄位的對齊,這會影響最短欄位的 byte 長度
Pack: 資料欄位的對齊,這會影響最短欄位的 byte 長度
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct PollResponse
{
public int AppId;
public byte Serial;
public short Station;
}
void Main()
{
var data = new PollResponse
{
AppId = 1,
Serial = 2,
Station = 3,
};
Type type = typeof(PollResponse);
int size = Marshal.SizeOf(type);
var bytes = new byte[size];
/* struct to byte array */
IntPtr ptrIn = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(data, ptrIn, true);
Marshal.Copy(ptrIn, bytes, 0, size);
Marshal.FreeHGlobal(ptrIn);
BitConverter.ToString(bytes).Dump();
/* 01-00-00-00 - 02 - 03-00 */
/* byte array to struct */
IntPtr ptrOut = Marshal.AllocHGlobal(size);
Marshal.Copy(bytes, 0, ptrOut, size);
var result = (PollResponse)Marshal.PtrToStructure(ptrOut, type);
Marshal.FreeHGlobal(ptrOut);
result.Dump();
/* { AppId = 1, Serial = 2, Station = 3 } */
}
2019-07-22 13:52
C# 用 Lambda 設定 MVC Route Constraint
MVC 的 Route Constraint 支援 Regular Pattern (string) 以及 IRouteConstraint,簡單的限制還可以用 Regular 處理,複雜的就需要實作 IRouteConstraint 了,既然都是一次工,索性就想要搭配 Lambda 讓設定可以更彈性的點。
順便增加了 HttpContextBase 的處理
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { controller = new ValueConstraint(x => x.ToUpper() != "WCF") }
);
public class ValueConstraint : IRouteConstraint
{
private Func<string, bool> _match;
public ValueConstraint(Func<string, bool> match)
{
_match = match;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return _match("" + values[parameterName]);
}
}
順便增加了 HttpContextBase 的處理
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { authenticated = new HttpConstraint(x => x.Request.IsAuthenticated) }
);
public class HttpConstraint : IRouteConstraint
{
private Func<HttpContextBase, bool> _match;
public HttpConstraint(Func<HttpContextBase, bool> match)
{
_match = match;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return _match(httpContext);
}
}
2019-07-21 17:21
C# 讓 Dequeue 更方便的擴充方法
void Main()
{
var queue = new Queue<int>();
for (int i = 0; i < 10; i++) { queue.Enqueue(i); }
string.Join(",", queue).Dump(); /* 0,1,2,3,4,5,6,7,8,9 */
var take = queue.EnumerateDequeue().Take(4).ToList();
string.Join(",", take).Dump(); /* 0,1,2,3 */
string.Join(",", queue).Dump(); /* 4,5,6,7,8,9 */
var take2 = queue.EnumerateDequeue().Take(40).ToList();
string.Join(",", take2).Dump(); /* 4,5,6,7,8,9 */
string.Join(",", queue).Dump(); /* */
}
public static class QueueExtensions
{
public static IEnumerable<T> EnumerateDequeue<T>(this Queue<T> source)
{
while (source.Count > 0) { yield return source.Dequeue(); }
}
public static IEnumerable<T> EnumerateDequeue<T>(this ConcurrentQueue<T> source)
{
T outValue;
while (source.TryDequeue(out outValue)) { yield return outValue; }
}
}
2019-07-21 17:06
C# 在 Enum 上增加附加資訊
C# 的 Enum 是個很方便的類型,如果可以再增加額外的資訊就更方便了,這裡利用 Attribute 去定義 Enum 額外的資訊,再用擴充方法取得 Enum 所屬的資訊。
用 Attribute 來定義有個好處,未來在增減 Enum 時可以一起進行修改,不用擔心會有遺漏而沒修改的問題。
用 Attribute 來定義有個好處,未來在增減 Enum 時可以一起進行修改,不用擔心會有遺漏而沒修改的問題。
void Main()
{
PortAreaCode.F1Front.GetFloor().Dump(); /* F1 */
}
public enum PortAreaCode
{
[AreaMeta("None", 0)]
None,
[AreaMeta("F1", 1)]
F1Front,
[AreaMeta("F2", 1)]
F2Front,
}
/// <summary>PortAreaCode 額外附屬資訊定義的 Attribute</summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
class AreaMetaAttribute : Attribute
{
public string Floor { get; private set; }
public int WarehouseId { get; private set; }
public AreaMetaAttribute() : this("None", 0) { }
public AreaMetaAttribute(string floor, int warehouseId)
{
Floor = floor;
WarehouseId = warehouseId;
}
}
/// <summary>PortAreaCode 的擴充方法</summary>
public static class PortAreaCodeExtensions
{
private static AreaMetaAttribute _defaultMeta = new AreaMetaAttribute();
private static AreaMetaAttribute getMeta(PortAreaCode value)
{
FieldInfo field = typeof(PortAreaCode).GetField(value.ToString());
if(field == null) { return _defaultMeta; }
var meta = field.GetCustomAttribute<AreaMetaAttribute>();
return meta ?? _defaultMeta;
}
public static string GetFloor(this PortAreaCode value)
{
return getMeta(value).Floor;
}
public static int GetWarehouseId(this PortAreaCode value)
{
return getMeta(value).WarehouseId;
}
}
2019-07-21 16:29
C# 用 gzip 壓縮字串並取得 base64 字串
這個使用方式的效果是有但書的,當 Source 的重複率不高壓縮的效果就不會好,再加上 base64 就是用可見文字去表示 byte 值,這會讓 base64 後的結果比 byte array 還要長,所以壓縮率沒有到達一定的程度下,輸出反而會比 Source 的字串還要長。
//using System.IO.Compression;
void Main()
{
string text = "OptionPostal,OptionClassType,OptionTalentItem";
text.Length.Dump(); /* 45 */
string compressBase64 = compress(text);
compressBase64.Length.Dump(); /* 76 */
compressBase64.Dump();
/* H4sIAAAAAAAEAPMvKMnMzwvILy5JzNHxB3OccxKLi0MqC1Kh/JDEnNS8Es+S1FwAaY6qVC0AAAA= */
string decompressText = decompress(compressBase64);
decompressText.Dump();
/* OptionPostal,OptionClassType,OptionTalentItem */
text = "OptionPostal,OptionClassType,OptionTalentItem,OptionPostal,OptionClassType,OptionTalentItem,OptionPostal,OptionClassType,OptionTalentItem,OptionPostal,OptionClassType,OptionTalentItem";
text.Length.Dump(); /* 183 */
compressBase64 = compress(text);
compressBase64.Length.Dump(); /* 84 */
compressBase64.Dump();
/* H4sIAAAAAAAEAPMvKMnMzwvILy5JzNHxB3OccxKLi0MqC1Kh/JDEnNS8Es+S1FyowCBQDQBPmlWktwAAAA== */
}
/*壓縮*/
private static string compress(string text)
{
if (string.IsNullOrEmpty(text)) { return text; }
byte[] buffer = Encoding.UTF8.GetBytes(text);
using (var outStream = new MemoryStream())
using (var zip = new GZipStream(outStream, CompressionMode.Compress))
{
zip.Write(buffer, 0, buffer.Length);
zip.Close();
string compressedBase64 = Convert.ToBase64String(outStream.ToArray());
return compressedBase64;
}
}
/*解壓縮*/
private static string decompress(string compressed)
{
if (string.IsNullOrEmpty(compressed)) { return compressed; }
byte[] buffer = Convert.FromBase64String(compressed);
using (var inStream = new MemoryStream(buffer))
using (var outStream = new MemoryStream())
using (var zip = new GZipStream(inStream, CompressionMode.Decompress))
{
zip.CopyTo(outStream);
zip.Close();
string text = Encoding.UTF8.GetString(outStream.ToArray());
return text;
}
}
2019-07-21 15:53
產生 IP v6 的 mask byte array
int length = 121; /* total 128 */
var mask = new byte[16];
for (int i = 0; i < 16; i++)
{
mask[i] = 0xff;
if (length > -8) { length -= 8; }
if (length < 0) { mask[i] = (byte)(mask[i] << -length); }
/* 當 length 出現負值時代表需要進行位移 */
}
BitConverter.ToString(mask).Dump();
/* FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-80 */
訂閱:
文章 (Atom)