为什么 Startup 不选择 .NET 作为后端语言
Reddit
上的一个讨论
为什么创业公司不选择 .NET
高赞回答列出了下面几个原因
- 创业公司吸引年轻人,而年轻人更加喜欢 Javascript
- 创业公司通常是全栈,Javascript 适合全栈开发
- 对于
C#
和.NET
有着传统的看法 - 过去一段事件
.NET
社区一些错误决定 .NET
这种基于约束的方法很难让人喜欢- 人力资源方面的挑战
- 不喜欢多线程
- 面向对象编程的恐惧
- 创业公司喜欢 Mac 电脑
- 认为
.NET
和Java
太主流了
1、C# 学习证书
微软联合 freecCodeCamp
推出了 C#
培训课程,在其中可以获得相关的证书。
10 月 4 号,Visual Studio
团队会组织一个茶话会,邀请团队人来分享关于 Visual Studio
的其他不为人知的方面,比如如何将 idea 转换为 feature,如何 debug,如何用 Visual Studio 创建移动应用程序。
在数据库应用程序中,我们通常需要考虑并发问题。比如一个酒店订阅系统,我们不能在同一个时间段将一个房间分发给两个订单。应用程序的逻辑一般是这样的
- 查询特定房间在特定时间段的订阅情况
- 如果被预定,返回订阅失败
- 否则,调用付款等其他服务
- 更新房间信息并且保存
这些步骤不是在同一个 Transaction
完成的,这就带来一个问题,如果在第 1 步之后,房间被其他用户约定了,那么在第 4 步的时候,就会覆盖前面一个订阅的结果。
那么 SQL Server
中的乐观锁就能解决这个问题,对于表中的都会内置一个 rowversion
列,当这一行被更新后,就会自动更改,那么过程就变成这样
- 查询特定房间在特定时间段的订阅情况
- 如果被预定,返回订阅失败
- 否则或者该行数据和
rowversion
,调用付款等其他服务 - 在更新的时候只有
rowversion
和之前上一步获取的相同的时候,才更新成功,否则失败。
体现在代码中是这样的
public class Apartment
{
public Guid Id { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
或者在 Fluent API
中
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Apartment>()
.Property<byte[]>("Version")
.IsRowVersion();
}
这样在调用 SaveSchanges
方法的时候,会抛出 DbUpdateConcurrencyException
异常
前一阵子 Moq
库的 SponserLink
的事情在 .NET
社区掀起了巨大的讨论。在事件平息之后,作者采访了 Moq
的作者聊了一下这件事的来龙去脉,包含了下面的话题
- 作者对开源认识
- 使用
SponerLink
的效果 - 事件发生后
Moq
的现状 - 对后续的规划和展望
C# 作为一个托管的编程语言,也就是说我们并不需要关心内存的问题。但是有很多情况下会导致内存泄漏,比如事件的的委托。
public class Publisher
{
public event Action<string> OnChange = delegate { };
public void Raise(){
OnChange("hello world");
}
public string Label => "Hi";
}
public class Subscriper : IDisposable
{
private bool disposed = false;
public Subscriper() {
Console.WriteLine("ctor");
}
~Subscriper() {
Console.WriteLine("descrt");
Dispose(false);
}
public void Dispose(){
Console.WriteLine("dispose");
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed) {
disposed = true;
}
}
public void WriteLine(string s){
Console.WriteLine(s);
}
}
比如这里定义了一个 Puber
和一个 Suber
类,那么在应用程序中
Subscriper sub;
Publisher publisher = new Publisher();
for (int i = 0; i < 20; i ++)
{
sub = new Subscriper();
// publisher.OnChange += sub.WriteLine;
}
Console.ReadKey();
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine();
Console.WriteLine(publisher.Label);
当调用 GC.Collect
之后,~Subscriper
方法就会被多次执行,因为被 GC 给回收。但是如果我们将 publisher.OnChange += sub.WriteLine;
恢复,所有的 ~Subscriper
都不会执行,因为从 GC
角度来看,它注册了一个 Puber
的事件。这样导致了内存泄露。
在 C#
中有一个叫做 WeakReference
类型,它能够跳过在 GC
对标记阶段。该类型有两个重要属性 IsAlive
表明引用的对象是否活着,Target
能够获得该对象。
public class WeakEvent
{
private readonly List<WeakReference> _listeners = new List<WeakReference>();
public void AddListener(Action<string> handler)
{
_listeners.Add(new WeakReference(handler));
}
public void Raise()
{
for(int i = _listeners.Count - 1; i >= 0; i--)
{
WeakReference wr = _listeners[i];
if (wr != null)
{
if (wr.IsAlive)
{
((Action<string>)wr.Target)("hell world");
}
else
{
_listeners.RemoveAt(i);
}
}
}
}
}
public class Publisher
{
private readonly WeakEvent _event = new WeakEvent();
public void Raise(){
_event.Raise();
}
public void Register(Action<string> handler){
_event.AddListener(handler);
}
public string Label => "Hi";
}
public class Subscriper : IDisposable
{
private bool disposed = false;
public Subscriper(){
Console.WriteLine("ctor");
}
~Subscriper(){
Console.WriteLine("descrt");
Dispose(false);
}
public void Dispose(){
Console.WriteLine("dispose");
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose managed resources here
}
// Dispose unmanaged resources here
disposed = true;
}
}
public void WriteLine(string s)
{
Console.WriteLine(s);
}
}
这时候,我们在调用下面这段代码的时候,由于 sub
的 WriteLine
注册对象是 WeakReference
, 那么 GC 就会将他们全部回收。
Subscriper sub;
Publisher publisher = new Publisher();
for (int i = 0; i < 20; i ++)
{
sub = new Subscriper();
publisher.OnChange += sub.WriteLine;
}
Console.ReadKey();
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine();
Console.WriteLine(publisher.Label);
一年一度的 .NET
形成提升的文章又来了,今年轮到 .NET 8
。这是一篇接近 20 页的文章,如果你有时间,可以快速浏览一下。
前一阵子微软宣布明年将不再支持 Visual Studio for Mac
, 当时引起了巨大的争论,现在作者分享了它对这件事情的看法
- 为什么微软创建
.NET
? 答案就是挣钱,不管是买授权,还是和其他产品绑定,或者做咨询甚至是获取声誉,都是为了挣钱。 Visual Studio
并不是一个天生跨平台的软件,有很多 windows 上的依赖。VS Code
是在 Linux 和 Mac 上很好的选项- 这个决定看上去太着急了,尤其对于 MAUI 的发展
- 微软需要更多的
Why
这是一些列博客,作者介绍了如何将 ASP.NET Framework
的应用程序迁移到 ASP.NET Core
, 作者采取了一种渐进式的迁移方式。通过一个空的 ASP.NET Core
的应用程序,然后将请求转发原本的应用程序,然后逐步迁移其他 Controller.
在数据库中,我们通过外键的方式将不同的表链接起来,在 EF Core 中,我们可以通过这种方式来多表查询,主要有三种方式
- Eager loading
这种方式是通过 Include
语句来包含子表
var invoices = db.Invoices
.Include(invoice => invoice.InvoiceLines)
.ToList();
// All invoices are already loaded...
foreach (var invoice in invoices)
{
// ...including all their Invoice lines
foreach (var invoiceLine in invoice.InvoiceLines)
{
// ...
}
}
- Lazy loading 这种方式只有在进行对子表属性访问的时候,才会发送查询请求
var invoices = db.Invoices
.ToList();
// All invoices are already loaded...
foreach (var invoice in invoices)
{
// ...invoice lines are queried when accessed...
foreach (var invoiceLine in invoice.InvoiceLines)
{
// ...the related product is also queried when accessed
var product = invoiceLine.Product;
// ...
}
}
- Explicit loading
通过 Load()
方法显示的获取相应的子表
var invoices = db.Invoices
.ToList();
// All invoices are already loaded...
foreach (var invoice in invoices)
{
// ...but you'll have to explicitly load invoice lines when they are needed
db.Entry(invoice).Collection(p => p.InvoiceLines).Load();
foreach (var invoiceLine in invoice.InvoiceLines)
{
// ...
}
}