Örnek projeyi github üzerinden indirmek veya görüntülemek için https://github.com/ayzdru/AspNetCSLAExamples adresine gidebilirsiniz.

Component-based Scalable Logical Architecture (CSLA) Nedir?
Rockford Lhotka tarafından yazılmış CSLA, yeniden kullanılabilir, bakımı yapılabilir, nesne yönelimli business katmanları oluşturmamızı sağlayan yazılım geliştirme yapısıdır.
CSLA Özellikleri
Smart data
Bir business object, temsil ettiği nesneyle ilişkili tüm verileri ve davranışı (business logic ve rules) encapsulate eder. Örneğin, bir SiparisDuzenle nesnesi, kullanıcının sipariş bilgilerini doğru bir şekilde düzenlemesine izin vermesi için gerekli verileri ve iş kuralı uygulamalarını içerecektir.
Rules engine
CSLA .NET framework, validation rules, business rules ve authorization kurallarını destekleyen bir rule engine sağlar. Bu kurallar nesne örneklerine veya özelliklerine eklenir ve gerektiğinde CSLA .NET tarafından otomatik olarak çağrılır. Validation rules, CSLA .NET rule engine kullanılarak veya Microsoft .NET'in DataAnnotations özelliği kullanılarak uygulanabilir.
Object persistence
Veri oluşturma, alma, güncelleme ve silme (CRUD), veri testiyle ilişkili iş nesnesinin açıkça tanımlanmış yöntemleriyle gerçekleştirilir. Veri erişimi, repository pattern veya object-oriented programming teknikleri kullanılarak, business mantığından açık bir şekilde ayrılır.
Metastate maintenance
CSLA .NET her bir business object için metastate'i yönetir. Örneğin, her bir business nesnesinin ne zaman yeni olduğu (henüz kaydedilmemiş olduğunu) ve ne zaman değiştirildiğini bilgilerini yönetir.
n-Level undo
Yapılan değişiklikleri geri almamızı sağlar. Bu bir uygulamada birden çok kez geri alma işlemleri yaptığımız yerlerde, çok yararlı olabilir.
Business rule tracking
Örneğin, bir Account objesinin PhoneNumber property alanı varsa ve bu alana alfabetik karakterlerle bir telefon numarası atanmışsa, Account nesnesinin IsValid özelliği false olacak (veritabanına kaydetmeyi imkansız hale getirecek) ve ardından yeni bir BrokenRule objesi olacaktır. Account'un Broker Rules listesine eklenecektir. Geçersiz telefon numarası düzeltildiğinde, bu kural ortadan kalkar ve Account objesi kendisini veritabanına kaydedebilir.
Ek Özellikler
CSLA .NET ek olarak;
Basit kullanıcı arayüzü oluşturma, Dağıtılmış veri erişimine, Web Services desteği özelliklerine sahiptir.

CSLA, 1997 yılından bugüne geliştirilmeye devam etmektedir.

CSLA, Windows Forms, ASP.NET, WPF, UWP, iOS, Android ve MacOS platformlarını destekler.
Kodlamaya Başlamadan Önce
Örnek projeyi github üzerinden indirmek veya görüntülemek için https://github.com/ayzdru/AspNetCSLAExamples adresine gidebilirsiniz.
AspNetCSLAExamples.Core adlı .Net Standart projesi ve AspNetCSLAExamples.Infrastructure .Net projesini ortak kullanacağımız için bu katmanların ne işe yaradığını açıklayacağım.
AspNetCSLAExamples.Core katmanına Entities, Interfaces nesnelerini ekledik.
AspNetCSLAExamples.Infrastructure katmanına DbContext, Repositories ve altyapı için gereken nuget paketlerini yükledik.
AspNetCSLAExamples.Business katmanına CSLA .Net için gereken business nesnelerini ekledik.
Örnek PersonList.cs business nesnesi:
[Serializable]
public class PersonList : ReadOnlyListBase<PersonList, PersonInfo>
{
[Create, RunLocal]
private void Create() { }
[Fetch]
private void Fetch([Inject]IPersonRepository dal)
{
IsReadOnly = false;
var data = dal.Get().Select(d => DataPortal.FetchChild<PersonInfo>(d));
AddRange(data);
IsReadOnly = true;
}
}
CSLA .NET ve Blazor Web Assembly
AspNetCSLAExamples.Blazor.Client SPA projemize Person tablosuna CRUD işlemleri için .razor sayfalarını (EditPerson.razor, ListPersons.razor, TextInput.razor) oluşturduk. Daha sonra
AspNetCSLAExamples.Blazor.Client > Program.cs dosyasını düzenliyoruz.
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.UseCsla((c) =>
c.DataPortal().DefaultProxy(
typeof(Csla.DataPortalClient.HttpProxy), "/api/DataPortal"));
await builder.Build().RunAsync();
}
}
AspNetCSLAExamples.Blazor.Server > Startup.cs dosyasını düzenliyoruz.
public class Startup
{
private const string BlazorClientPolicy = "AllowAllOrigins";
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(BlazorClientPolicy,
builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddMvc(
(o) => o.EnableEndpointRouting = false)
.AddNewtonsoftJson();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
services.AddMvc((o) => o.EnableEndpointRouting = false);
services.Configure<KestrelServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
services.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
services.AddHttpContextAccessor();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddControllersWithViews();
services.AddRazorPages();
services.AddTransient(typeof(IPersonRepository), typeof(PersonRepository));
services.AddCsla();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
app.UseCsla();
}
}
AspNetCSLAExamples.Blazor.Server > DataPortalController.cs dosyasını oluşturduk.
[Route("api/[controller]")]
[ApiController]
public class DataPortalController : Csla.Server.Hosts.HttpPortalController
{
public DataPortalController()
{
UseTextSerialization = true;
}
[HttpGet]
public string Get()
{
return "Running";
}
public override Task PostAsync([FromQuery] string operation)
{
return base.PostAsync(operation);
}
}
CSLA .NET ve Razor Pages
AspNetCSLAExamples.RazorPages Web projemize Person tablosuna CRUD işlemleri için .cshtml sayfalarını (Delete.cshtml, Edit.cshtml, Index.cshtml) oluşturduk. Daha sonra
Startup.cs dosyasını düzenliyoruz.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddRazorPages();
services.AddHttpContextAccessor();
services.AddCsla();
services.AddTransient(typeof(IPersonRepository), typeof(PersonRepository));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
app.UseCsla();
Configuration.ConfigureCsla();
}
}
CSLA .NET ve MVC
AspNetCSLAExamples.Mvc Web projemizde Ekleme, Çıkarma, Silme, Güncelleme, Detay Gösterme için Viewslerimizi (Create, Delete, Details, Edit) oluşturduk. Daha sonra
PersonController.cs dosyasını oluşturuyoruz.
public class PersonController : Csla.Web.Mvc.Controller
{
// GET: Person
public async Task<ActionResult> Index()
{
var list = await DataPortal.FetchAsync<PersonList>();
return View(list);
}
// GET: Person/Details/5
public async Task<ActionResult> Details(int id)
{
var obj = await DataPortal.FetchAsync<PersonInfo>(id);
return View(obj);
}
// GET: Person/Create
public async Task<ActionResult> Create()
{
var obj = await DataPortal.CreateAsync<PersonEdit>();
return View(obj);
}
// POST: Person/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(PersonEdit person)
{
try
{
if (await SaveObjectAsync<PersonEdit>(person, false))
return RedirectToAction(nameof(Index));
else
return View(person);
}
catch
{
return View(person);
}
}
// GET: Person/Edit/5
public async Task<ActionResult> Edit(int id)
{
var obj = await DataPortal.FetchAsync<PersonEdit>(id);
return View(obj);
}
// POST: Person/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(int id, PersonEdit person)
{
try
{
LoadProperty(person, PersonEdit.IdProperty, id);
if (await SaveObjectAsync<PersonEdit>(person, true))
return RedirectToAction(nameof(Index));
else
return View(person);
}
catch
{
return View(person);
}
}
// GET: Person/Delete/5
public async Task<ActionResult> Delete(int id)
{
var obj = await DataPortal.FetchAsync<PersonInfo>(id);
return View(obj);
}
// POST: Person/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Delete(int id, PersonInfo person)
{
try
{
await DataPortal.DeleteAsync<PersonEdit>(id);
return RedirectToAction(nameof(Index));
}
catch
{
return View(person);
}
}
}
Startup.cs dosyasını düzenliyoruz.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddControllersWithViews((c) =>
c.ModelBinderProviders.Insert(0, new Csla.Web.Mvc.CslaModelBinderProvider()));
services.AddHttpContextAccessor();
services.AddCsla();
services.AddTransient(typeof(IPersonRepository), typeof(PersonRepository));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
app.UseCsla();
}
}
Daha detaylı incelemek için, projeyi github üzerinden indirip, kendi lokalinizde çalıştırabilir, düzenleyebilirsiniz.
Mutlu kodlamalar
༼ つ ◕_◕ ༽つ