סרוויסים: wcf, webapi, odata וכו'

סרוויסים: wcf, webapi, odata וכו'

שלום וברכה.

1. תיאור המקרה
המקרה פשוט מאד: יישום שרת-לקוח, כשהלקוח הוא WPF. אני מפתח את השרת והלקוח באותו מחשב, באותו סולושיין, ואחר כך פורס את הסרוויס לIIS, ואת הקליינט למחשב היעד.

א. בתחילה בניתי את הסרוויס בWCF, ועשיתי בקליינט סרוויס-רפרנס. הכל טוב ויפה.
ב. אחר כך רציתי להוסיף כלכולייטד פרופרטיז למודלים, וזה לא אפשרי אם המודלים מוגדרים בסרוויס. [הסרוויס-רפרנס יוצר בקליינט מודלים עצמאיים, שאינם מכירים את הקוד של המודלים שבסרוויס]
לכן הוספתי פרוייקט שלישי - DAL - שבו מוגדרים המודלים, והוספתי רפרנס גם לסרוויס וגם לקליינט לפרוייקט הDAL.
גם זה עבד טוב ויפה, אם כי שאינני יודע האם זו הדרך הסטנדרטית.

ג. כל זה היה טוב ונוח כאשר הסרוויס היה בעיקר דוחות מנורמלים ומעובדים. אבל עכשיו אני צריך להוסיף גם גישה למודלים עצמם, CURD, ובעיקר תמיכה בLINQ.
למשל: בקליינט יש טופס טרנזקציות, והמשתמש יכול לפלטר לפי תאריך, סטטוס, שם וכו'. הפילטרים אמורים לעבור לסרוויס, שיפלטר את הORM.

השאלה שלי איך לעשות את זה.

2. דיון
ד. מחיפוש ברשת למדתי שיש דבר שנקרא OData שאמור לספק את הפונקציונליות הזו, והשילוב של זה בתוך WCF נקרא WCF Data Services, שהיום הוא כבר מיושן ולא מפותח. במקום זה, עדיף להשתמש בWebApi.
ה. פה בפורום, היה לא מזמן דיון בענין, ומשום מה זה הוצג כWebApi vs OData. לכאורה, לפי מה שראיתי, אינם דברים שונים, אלא OData מספק את הפונקציונליות הנדרשת לWebApi.

אודה לכם אם תעשו לי סדר, ותמליצו לי על הגישה הטובה

בברכה ובתודה.
 

Royi Namir

New member
תוכל לתת דוגמא חיה?

היי ,
אתה יכול לשלוח ל WEBAPI הכל
הבנתי ש ODATA זה עוד שכבת פילטור שאתה שולח
אבל לא יודע עד כמה זה מתוחזק היום ( עם דגש על לא יודע)


תן דוגמא אמיתי ( עד כמה שאפשר)
 
דוגמא חיה

public class Transaction{
public int Id { get; set; }
public int ProductId { get; set; } public int CustomerId { get; set; } public DateTime TranTime { get; set; } public decimal Price { get; set; }}public class TrannsactionListViewModel{ readonly IMyService service; public TrannsactionListViewModel(IMyService service) { this.service = service; } public int? ProductSelector { get; set; } public int? CustomerSelector { get; set; } public DateTime? TranTimeStart { get; set; } public DateTime? TranTimeEnd { get; set; } public List<Transaction> GetTransaction() { IQueryable<Transaction> tranQuery = service.GetTransaction(); if (ProductSelector.HasValue) tranQuery = tranQuery.Where(t => t.ProductId == ProductSelector.Value); if (CustomerSelector.HasValue) tranQuery = tranQuery.Where(t => t.CustomerId == CustomerSelector.Value); if (TranTimeStart.HasValue) tranQuery = tranQuery.Where(t => t.TranTime >= TranTimeStart.Value); if (TranTimeEnd.HasValue) tranQuery = tranQuery.Where(t => t.TranTime >= TranTimeEnd.Value); List<Transaction> result = tranQuery.ToList(); return result; }}

השאלה שלי היא: מי מתאים להיות IMyService.

באשר לתחזוקה של WebApi, נראה לי שזה די מתוחזק, גירסאות חדשות יוצאות וגם מדריכים יפים ופשוטים באתר AspNet.
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api
 

arik23m

New member
פרשת דרכים

היי לא אמור DLL משותף כזה בין שני הרכיבים
הקליינט צריך להיות הכי טיפש שאפשר
ולהשתמש באובייקטים ובמתודות של הסרביס
&nbsp
אתה בצומת הנכונה לבחור בין ODATA לבין WEBAPI
לשניהם שוחררה גרסאת עדכון ביחד החודש
http://bit.ly/1wnpdrS
ונראה ששניהם מתוחזקות במקביל
אבל במקרה חדשתי קשר עם אחד הישראלים הכי חזקים בתחום הODATA בארץ (עוד כשהתחום היה בוסרי)
קוראים לו גיל פינק http://blogs.microsoft.co.il/gilf
שאלתי את שאלתך והוא המליץ ללכת על WEB API דרכם גם אתה יכול להחזיר ODATA
בהצלחה
&nbsp
 
תודה

אבל אינני מבין למה אתה מעמיד את OData מול WebApi.
זה כמו לשאול האם לפתח באנגולר או בJS.

לפי מה שראיתי, OData מספק פונקציונליות. הוא יכול לספק אותה לWcf וגם לWebApi.

תודה על ההבהרה באשר לDLL המשותף. הבעיה היא שאין טעם לתחזק את המודלים בשני פרוייקטים שונים. בWCF, מוסיפים סרוויס-רפרנס, וזה יוצר את המחלקות. אבל בWebApi, אין את היכולת הזו.
אז הנה עוד סיבה להשתמש בOData, כי הוא כולל גנרטור שיוצר בצד הלקוח את המחלקות של הסרוויס. אולי גם לWebApi יש את זה. אינני יודע.
 

arik23m

New member
>>

אני טרם מכיר את הנושא לעומק - אם יש לך שאלות נוספות מציע לך ליצור קשר עם גיל
&nbsp
 
I vote for OData

אחרי 24 שעות של OData, אני בהחלט בעד. אפשר להגדיר את Odata במילה אחת: Linq2Http.

RoyiNamir תמה "למה צריך את OData, הרי כלום לא חסר בWebApi".

ובכן, לפני כמה שנים היו שטענו "בשביל מה צריך Linq2Sql, כלום לא חסר בAdo.Net", וגם פרסמו השוואות שמוכיחות את הפרפורמנס הירוד של ORM. ובכל זאת, היום אף אחד לא מוותר על ORM, ועל Linq.
אז ORM עובד היטב בקטע שבין הדטהבייס לסרוויס, ושם זה נתקע. הקליינט לא יכול להריץ Linq מול הסרבר.
ועתה בא OData, וסוגר גם את הפינה הזו: אפשר לכתוב Linq [או JS] בצד לקוח, וזה עובר לסרבר שמעביר את זה לדטהבייס.

אודה על האמת, שלי זה נראה די מסובך, בגלל שאני לא רגיל לעבוד עם WebApi. אבל מי שכבר יודע, בוודאי יסתגל מהר ועקומת הלמידה שלו תהיה חדה מאד.
ובאשר לתחזוקה, OData מתוחזק יפה מאד. לדוגמא: הבלוג של Odata Team מפרסם פוסט מדי שבוע.
 
מסכים אתך ברוב הדברים

הרעיון - פשוט מעולה. אפשרות לבצע שאילתות ברמת http.
נכון מה שרועי אמר (וגם אמר לי בפעמים קודמות שהנושא עלה לדיון) שיש לך הכל ב webapi, אבל! בהרבה יותר עבודה.
אתה צריך לעשות endpoint לכל סוג של שאילתה, וזה מאד לא גמיש. האפשרות לעשות סרביס אחד שמשרת כל שאילתה שיכולה לעלות על הדעת (על ישות מסוימת, והילדים שלה), היא אדירה.
הבעיה, שיש בעיות במימוש. סתם לדוגמה, דבר שניתקלתי בו לפני כמה שבועות: odata לא תומך בשדה datetime, אלא רק במשהו חדש שנקרא datetimeoffset . עכשיו, להמיר את כל הדטהבייס שלי בגלל החמורים האלה, לא נראה לי הגיוני. וכל כמה זמן קופץ איזה שיגעון חדש. אמנם יש תמיכה, אבל היא מאד לקויה וחלקית, ועדכונים מגיעים לעיתים רחוקות. יש איזה ועדה שדנה בשינויים... בקיצור, בתחושה שלי זה קרוב מאד לבשלות ולשימוש בפרודקשן, אבל אנחנו עוד לא ממש שם. אני מאד רוצה להשתמש בזה, אבל עדיין יש מגבלות. זה ברשימת ה TODO שלי לעשות ניסיון נוסף בזמן הקרוב.
&nbsp
 

Royi Namir

New member
מממ.

תהיתי לא בקטע של לשלול אלא יותר בקטע של ללמוד
אני מכיר דיי טוב WEBAPI ( ללא ODATA) ואני רוצה להגיד לך שODATA זה רעיון יפה אבל אני עדין לא רואה את ה MAGNITUDE שאתה מנסה להשוות כאן - כמו המעבר בין ADO ל LINQ2SQL או יותר נכון EF.

המעבר של -
לכתוב שאילתת SQL ללא INTELLISENSE ללא חיבור ל ENTITY ב SQL ,בלי לדעת אם היית יכול לכתוב את זה יותר טוב ב SSMS

אל -
צורה שכבר עם אופטימיזצית אופרטורים , יחד עם הפונקציות של #C , יחד עם INTELLISENSE

ממש לא שקולה להכנסת מודול פילטורים ל WEBAPI שזו מפלצת בעצמה.

דרך אגב : "היום אף אחד לא מוותר על ORM" : לא תמיד, בחברה שהביצועים מאד חשובים שם ויש הרבה שליפות - אתה לא תשתמש ב ORM. אולי ב DAPPER. אבל זה לא באמת ORM.(בקטנה)
 
=> זה Linq ממש

[תשובה לרועי ודוד, בגלל ה"פריצה המזעזעת" לא הצלחתי לשרשר במקום]

OData מאפשר שימוש בLinq, וזה לא רק "מודול פילטורים לWebApi".

הנה קונטרולר פשוט של OData:
// Controller in serverpublic class ProductsController : ODataController
{
ProductsContext db = new ProductsContext(); [EnableQuery] public IQueryable<Product> Get() { return db.Products; }}// clientvoid ListAllProducts(Container container){ foreach (var p in container.Products) { Console.WriteLine("{0} {1} {2}", p.Name, p.Price, p.Category); }}
דוד גילי, אתה כמובן לא אמור לשנות את השדות בדטהבייס. הקליינט עובד עם דייטטייםאופסט, והספריה מתרגמת את זה לדייטטיים.
 

Royi Namir

New member
איפה הלינק פה ? קליינט ששולח פילטור , משהו ?

היה נחמד אם היית נותן דוגמא שיותר מראה את הכוח של זה
לעומת הקונטרולר הדיי פשוט כאן, עם קריאה סטנדרטית של קליינט
 
אה, אתה צודק

התכוונתי להראות רק את העקרון שהשרת חושף Querable, והקליינט משתמש בו.
הנה דוגמא ללינק קלאסי:
var filtered = container.Products.Where(x => x.SupplierId== 124 && Name == "My name").OrderBy(x => x.ProductSupplierId).Take(25)

ודוד גילי צודק! הגירסא החדשה תואמת חלקית לדייטטיים: אפשר להחזיר רשומות, אבל לא לפלטר ולא למיין!
בקיצור, לא שווה כלום.
חבל.
 
שאל את דוד גילי

אינני יודע JS.
התוצאה הסופית צריכה להיות שרשור הפרמטרים למחרוזת.
אני מאמין שיש ספריות שעושות את זה. למשל, גילי אומר שהפקדים של טלריק יודעים לעשות זאת.
 
כן

עד שלא תהיה תמיכה בתאריך, אין לזה שימוש.
יש פתרונות, אבל אני רוצה "מחוץ לקופסה".

הפתרון שמצאתי לבעיה, [העברת Query לשרת] הוא שימוש בFilterDesriptor [זה אובייקט של טלריק]
אני שולח לשרת את הפילטרים, ושם מחיל אותם על האוסף.
 
אז איך בכל זאת לפלטר את הטבלאות?

מצאתי כאן פתרון מבריק:
http://blog.esskar.de/articles/2012/9/serialize_linq_expressions_over_wcf.html

כדי להקל על השימוש, כתבתי מחלקה קטנה:
public sealed class RemoteQuery<T> {
readonly Func<ExpressionNode, T[]> source;
Expression<Func<T, bool>> exp; public RemoteQuery(Func<ExpressionNode, T[]> source) { this.source = source; } public RemoteQuery<T> Where(Expression<Func<T, bool>> predicate) { if (exp == null) { exp = predicate; } else { var body = Expression.AndAlso(exp.Body, predicate.Body); var lambda = Expression.Lambda<Func<T, bool>>(body, exp.Parameters[0]); exp = lambda; } return this; } public T[] ToArray() { var node = exp.ToExpressionNode(); var vv = source(node); return vv; } }
ועכשיו, אני יכול לכתוב קוד קצר ופשוט:
var query = new Helpers.RemoteQuery<Product>(client.Products); Product[] products = views.Payments().Where(p => p.SupplierId > 0).Where(p => p.ProductId == 56).ToArray();
 
למעלה